Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use an enum to type an index signature of an interface field?

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];
like image 787
Flávio Lisbôa Avatar asked Nov 27 '18 21:11

Flávio Lisbôa


People also ask

What is an index signature typescript?

The index signature is a fitting way to handle objects with properties we know nothing about. Its syntax describes a regular property, but instead of writing a standard property name, we define the type of keys and the properties.

What is enum variable?

An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. Common examples include compass directions (values of NORTH, SOUTH, EAST, and WEST) and the days of the week.

Which type string has no matching index signature?

The error "No index signature with a parameter of type 'string' was found on type" occurs when we use a value of type string to index an object with specific keys. To solve the error, type the string as one of the object's keys using keyof typeof obj .

Is incompatible with index signature?

The error "Property is incompatible with index signature" occurs when a property is not compatible with the specified type of the index signature. To solve the error, change the type of the property or use a union to update the type in the index signature.


1 Answers

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
like image 63
captain-yossarian Avatar answered Nov 11 '22 08:11

captain-yossarian