Mustafa Yusuf
Mustafa Yusuf

Reputation: 160

How to convert a Typescript type to an enum?

I want to convert a custom Typescript type into an enum. Here is my type:

export type TradeRequest {
  status: 'trade',
  id: ItemId,
  slotNumber: number,
}

ItemId itself is an enum. This is what it looks like:

export enum ItemId {
  SHOVEL = 10000,
  SWORD = 10001,
  APPLE = 20000,
}

Here is the enum that I'm trying to create:

export enum TradeRequest {
  STATUS = 'item',
  ITEM_ID = ItemId,
  SLOT_NUMBER = number,
}

When I do this, I get this error:

Only numeric enums can have computed members, but this expression has 'typeof ItemId' If you do not need exhaustiveness checks, consider using an object literal instead.

Upvotes: 0

Views: 5130

Answers (1)

sno2
sno2

Reputation: 4183

You cannot compose enums with other enums. TypeScript enums are untagged. Therefore, they are simply values without context that you access by accessing the members with dot notation. You could try using a regular union like this:

export enum ItemId {
  SHOVEL = 10000,
  SWORD = 10001,
  APPLE = 20000,
}

type TradeRequest = "item" | ItemId | number;

However, ItemId's type is simply a union of the possible values - 10000 | 10001 | 20000. Therefore, the number type will eagerly consume it thus making the type compute to "item" | number. I advise you use a C-Style union as I have described creating in this answer. Basically, you have a type object that looks like an enum but it's just an object and you create it via object literals and it forces you to only allow one member. This allows us to tag your data so that the ItemId type is not consumed by number type:

export enum ItemId {
  SHOVEL = 10000,
  SWORD = 10001,
  APPLE = 20000,
}

// from https://stackoverflow.com/a/71476167/10873797
type CUnion<T extends Record<PropertyKey, unknown>>
  = { [K in keyof T]: { [_ in K]: T[K] } & { [_ in Exclude<keyof T, K>]?: undefined } }[keyof T];

type TradeRequest = CUnion<{
  status: 'trade',
  id: ItemId,
  slotNumber: number,
}>;

const foo1: TradeRequest = {
    id: ItemId.APPLE
};

const foo2: TradeRequest = {
    slotNumber: 2354,
};

const foo3: TradeRequest = {
    status: "trade"
};

// then you access it like this

if (foo1.id !== undefined) {
    // you know that it is the id variant
    foo1.id // ItemId
    foo1.slotNumber // undefined
    foo1.status // undefined
}

TypeScript Playground Link

Upvotes: 1

Related Questions