9

I can describe an indexed type restriction in an object type notation such as the following:

enum Enum {
    A = 0,
    B = 1,
}

type EnumMap = {
    [P in Enum]: string;
}

But, surprisingly, the same doesn't seem to be possible when using index notation in an interface:

enum Enum {
    A = 0,
    B = 1,
}

interface EnumMap {
    [P in Enum]: string;
}

The error is:

A computed property name must be of type 'string', 'number', 'symbol', or 'any'.

Is there any reason why this is so? By definition, enums in TypeScript can have only string or number values (or even both, but that's not recommended), and I thought the enum itself would work like a union type for all the values it lists.


Investigating a bit further, I also found that, in the following example, EnumValues has type number, instead of (what I expected to be) 0 | 1. Again, why is this so?

const Enum = {
    A: 0,
    B: 1
};

type EnumKeys = keyof typeof Enum;
type EnumValues = typeof Enum[EnumKeys];
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
Flávio Lisbôa
  • 651
  • 5
  • 19
  • I believe these answers will help you: https://stackoverflow.com/questions/64970414/typescript-assigning-an-interface-or-a-type-to-a-recordstring-string and https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types#answer-64971386 – captain-yossarian from Ukraine Jun 02 '21 at 08:14

1 Answers1

1

Regarding error in:

interface EnumMap {
    [P in Enum]: string;
}

Enum is a special data structure in TypeScript and it is not assignable to string | number | symbol. COnsider this example:

const key = <T extends string | number | symbol>(t: T) => t

key(Enum) // error

Also, enum has special behavior.

See this example:

const enm = (t: Enum) => t

// Argument of type 'typeof Enum' is not assignable to parameter of type 'Enum'
enm(Enum) // error

So there is a difference even between Enum and typeof Enum.

Let's go back to our problem. Until TS 4.4 you were no allowed to use unions as an index signature in interfaces. Consider this example without enum:

interface EnumMap {
    [P in 'a'|'b']: string; // error
}

Hence it is about TS restrictions an not about enums.

As for the second case:

const Enum = {
    A: 0,
    B: 1
};

type EnumKeys = keyof typeof Enum;
type EnumValues = typeof Enum[EnumKeys];

This is because const Enum is mutable.

In order to obtain 1|0, you should make it immutable:

const Enum = {
    A: 0,
    B: 1
} as const; // special syntax

type EnumKeys = keyof typeof Enum;
type EnumValues = typeof Enum[EnumKeys]; // 0 | 1