Since 2018, there is an easier way in Typescript, without using keyof typeof
:
let obj: { [key in MyEnum]: any} =
{ [MyEnum.First]: 1, [MyEnum.Second]: 2 };
To not have to include all keys:
let obj: { [key in MyEnum]?: any} =
{ [MyEnum.First]: 1 };
To know the difference between in
and keyof typeof
, continue reading.
in Enum
vs keyof typeof Enum
in Enum
compiles to enum values and keyof typeof
to enum keys.
With keyof typeof
, you cannot change the enum properties:
let obj: { [key in keyof typeof MyEnum]?: any} = { First: 1 };
obj.First = 1;
// Cannot assign to 'First' because it is a read-only property.
... unless you use -readonly
:
let obj: { -readonly [key in keyof typeof MyEnum]?: any} = { First: 1 };
obj.First = 1; // works
But you can use any integer key?!:
let obj: { [key in keyof typeof MyEnum]?: any} = { First: 1 };
obj[2] = 1;
keyof typeof
will compile to:
{
[x: number]: any;
readonly First?: any;
readonly Second?: any;
}
Note both the [x: number]
and the readonly
properties. This [x: number]
property doesn't exist with a string enum.
But with in Enum
, you can change the object:
enum MyEnum {
First, // default value of this is 0
Second, // default value of this is 1
}
let obj: { [key in MyEnum]?: any} = { [MyEnum.First]: 1 };
obj[MyEnum.First] = 1; // can use the enum...
obj[0] = 1; // but can also use the enum value,
// as it is a numeric enum by default
It's a numeric enum. But we can't use any number:
obj[42] = 1;
// Element implicitly has an 'any' type because
// expression of type '42' can't be used to index type '{ 0?: any; 1?: any; }'.
// Property '42' does not exist on type '{ 0?: any; 1?: any; }'.
The declaration compiles to:
{
0?: any;
1?: any;
}
We allow only 0
and 1
, the values of the enum.
This is in line with how you would expect an enum to work, there are no surprises unlike keyof typeof
.
It works with string and heterogenous enums:
enum MyEnum
{
First = 1,
Second = "YES"
}
let obj: { [key in MyEnum]?: any} = { [MyEnum.First]: 1, [MyEnum.Second]: 2 };
obj[1] = 0;
obj["YES"] = 0;
Here the type is:
{
1?: any;
YES?: any;
}
Get immutability with readonly
:
let obj: { readonly [key in MyEnum]?: any} = {
[MyEnum.First]: 1,
};
obj[MyEnum.First] = 2;
// Cannot assign to '1' because it is a read-only property.
... which makes these keys readonly
:
{
readonly 1?: any;
readonly 2?: any;
}
Summary
in Enum |
keyof typeof Enum |
Compiles to enum values |
Compiles to enum keys |
Does not allow values outside the enum |
Can allow numeric values outside the enum if you use a numeric enum |
Can change the object, immutability opt-in with readonly |
Can't change enum props without -readonly . Other numeric values outside the enum can be |
Use in Enum
if possible. If your codebase uses keyof typeof Enum
, be aware of these pitfalls.