Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In typescript, `number` can be assigned to `enum`, but `string` is not. why? [duplicate]

In typescript, number can be assigned to enum, but string is not... it looks weird. why works like this?

And is there some ts options related this?

enum MixEnum {
  HELLO = 'hello',
  WORLD = 2,
}

const ms1: MixEnum = String('foo'); // Type 'string' is not assignable to type 'MixEnum'.
const ms2: MixEnum = String('hello'); // Type 'string' is not assignable to type 'MixEnum'.

const mn1: MixEnum = Math.random(); // no error
const mn2: MixEnum = Number('99999'); // no error
const mn3: MixEnum = MixEnum.WORLD + 100; // no error

https://www.typescriptlang.org/play?#code/KYOwrgtgBAcpCi5oG8BQUoAl4BkcHkoBeKARgBp0oB1fAJRwBFioAmSgX1VQGMB7EAGcALlBAhSALlgIkLALIBDYQAsAdACdFIACZ8IACgCUAbl4CRYkK2lwIiSCzsAjYBoMByAJw+fH0+ZCouIAzNLyAJYAHg7QJHaxarQMzADUZAAMGWaoAPT5BYUF3KCOkTFyaBjYeIQkHirAADZNfB6UGMlMLOyoXIGWEBLh0bEKyupauvrGZvxBUEM2UOVj8ZCu7t6+Xv5zFqJDYSujciSrSEn03emkWTnzg4JSJxWOJADKwhoRIADmngAZnw2gFHodBMsLu8oF8fv9PI0WqCzEA

like image 734
Hooriza Avatar asked Sep 17 '25 14:09

Hooriza


1 Answers

TypeScript's number enums behave in a few strange ways. This behaviour, where an enum with any numeric member treats all numbers as though they are members, is one of them. Another to watch out for is reverse mappings, which can make the type of things like Object.keys(MyEnum) different to what you might normally expect.

Personally, I recommend avoiding TypeScript's built-in enums because of this sort of weirdness. It's possible to make your own enums using plain JavaScript objects with TypeScript's as const, and have them behave in almost exactly the same way. In fact, in this particular example they may behave better, because TypeScript will only consider the specific numbers you have specified to be members of your enum.

There are both upsides and downsides to making your own enums without the enum keyword:

+ The syntax is more familiar to JavaScript developers who are new to TypeScript

+ The generated JavaScript is more obvious, because only TypeScript annotations are removed

+ TypeScript won't generate reverse lookups for number enums, which can be unintuitive and cause unexpected problems

+ TypeScript won't treat anything with the type number as though it might be a member of a numeric enum

- TypeScript won't generate the type associated with an enum for you, so you need to create it yourself, making the code a bit more verbose

- Because the type is constructed as a union of constant types (e.g. 'a' | 'b'), you may not get as useful autocomplete in your IDE because it will suggest those values directly rather than suggesting you access them through your enum object

- You always need to specify a value for each key, whereas for numeric TypeScript enums you can omit the initialisers.


Personally, I use a utility EnumTypeOf type to assist with the downside of needing to construct a type manually. Aside from that downside, I find object-based enums easier and more intuitive to use.

type EnumTypeOf<T extends Record<string, unknown>> = T[keyof T];

const MixEnum = {
  HELLO: 'hello',
  WORLD: 2,
} as const;
type MixEnum = EnumTypeOf<typeof MixEnum>;

const ms1: MixEnum = String('foo'); // Type 'string' is not assignable to type 'MixEnum'.
const ms2: MixEnum = String('hello'); // Type 'string' is not assignable to type 'MixEnum'.

const mn1: MixEnum = Math.random(); // Type 'number' is not assignable to type 'MixEnum'.
const mn2: MixEnum = Number('99999'); // Type 'number' is not assignable to type 'MixEnum'.
const mn3: MixEnum = MixEnum.WORLD + 100; // Type 'number' is not assignable to type 'MixEnum'.

TypeScript Playground

like image 81
Mark Hanna Avatar answered Sep 20 '25 06:09

Mark Hanna