Support for this was added in TypeScript 2.9 as part of expanded key support mapped types. Specifically:
A mapped type
{ [P in K]: XXX }permits anyKassignable tostring | number | symbol.
That means you can make a type alias to a mapped type with enum values as the key:
enum MyEnum {
PROP_1 = "prop 1",
PROP_2 = "prop 2",
// ... more props
}
type MyEnumValuedKeys = { [K in MyEnum]: boolean; }
// identical to
// type MyEnumValuedKeys = {
// "prop 1": boolean;
// "prop 2": boolean;
// ... more props
// }
You can make mapped type properties optional by adding a ? after the index-like key:
type OptionalMyEnumValuedKeys = { [K in MyEnum]?: boolean; }
// identical to
// type MyEnumValuedKeys = {
// "prop 1"?: boolean;
// "prop 2"?: boolean;
// ... more props
// }
So you can use OptionalMyEnumValuedKeys as your PropertyInterface. It is a type alias and not an interface. That usually doesn't matter but in case it does, TypeScript 2.8 introduced the abilitity to extend certain concrete mapped types, so you can get an interface like this:
type OptionalMyEnumValuedKeys = { [K in MyEnum]?: boolean; };
interface PropertyInterface extends OptionalMyEnumValuedKeys {} // add no properties
This could also be accomplished by using the built-in Record and Partial type aliases:
interface PropertyInterface extends Partial<Record<MyEnum, boolean>> {}
Okay, hope that helps. Good luck!
Answer from jcalz on Stack OverflowAllow enums as object literal keys
TypeScript: typings based on object keys that come from enum - Stack Overflow
typescript - Use enum as restricted key type - Stack Overflow
Enum From Object Literal Keys
Support for this was added in TypeScript 2.9 as part of expanded key support mapped types. Specifically:
A mapped type
{ [P in K]: XXX }permits anyKassignable tostring | number | symbol.
That means you can make a type alias to a mapped type with enum values as the key:
enum MyEnum {
PROP_1 = "prop 1",
PROP_2 = "prop 2",
// ... more props
}
type MyEnumValuedKeys = { [K in MyEnum]: boolean; }
// identical to
// type MyEnumValuedKeys = {
// "prop 1": boolean;
// "prop 2": boolean;
// ... more props
// }
You can make mapped type properties optional by adding a ? after the index-like key:
type OptionalMyEnumValuedKeys = { [K in MyEnum]?: boolean; }
// identical to
// type MyEnumValuedKeys = {
// "prop 1"?: boolean;
// "prop 2"?: boolean;
// ... more props
// }
So you can use OptionalMyEnumValuedKeys as your PropertyInterface. It is a type alias and not an interface. That usually doesn't matter but in case it does, TypeScript 2.8 introduced the abilitity to extend certain concrete mapped types, so you can get an interface like this:
type OptionalMyEnumValuedKeys = { [K in MyEnum]?: boolean; };
interface PropertyInterface extends OptionalMyEnumValuedKeys {} // add no properties
This could also be accomplished by using the built-in Record and Partial type aliases:
interface PropertyInterface extends Partial<Record<MyEnum, boolean>> {}
Okay, hope that helps. Good luck!
It's been a long time since asked, but if you end up here this may be what you're after:
enum MyEnum {
PROP_1 = "prop 1",
PROP_2 = "prop 2",
// ... more props
}
interface MyInterface {
property: {
[MyEnum.PROP_1]: boolean,
[MyEnum.PROP_2]: boolean,
// ... more props
}
}
Source: https://github.com/Microsoft/TypeScript/issues/14682#issuecomment-286999967
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.
Other differences
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.
Yes. Just type
let layer:{[key in keyof typeof MyEnum]: any}
The keyof keyword is available since Typescript 2.1. See the TypeScript documentation for more details.
Using only keyof for enums wouldn't work (you'd get the keys of the enum type and not the enum constants), so you have to type keyof typeof.
Enums in TypeScript 0.9 are string+number based. You should not need type assertion for simple conversions:
enum Color{
Red, Green
}
// To String
var green: string = Color[Color.Green];
// To Enum / number
var color : Color = Color[green];
Try it online
I have documentation about this and other Enum patterns in my OSS book : https://basarat.gitbook.io/typescript/type-system/enums
Make sure to use this if you are using the typescript flag --noImplicitAny:
var color : Color = Color[green as keyof typeof Color];
As of Typescript 2.1 string keys in enums are strongly typed. keyof typeof is used to get info about available string keys (1):
enum Color{
Red, Green
}
let typedColor: Color = Color.Green;
let typedColorString: keyof typeof Color = "Green";
// Error "Black is not assignable ..." (indexing using Color["Black"] will return undefined runtime)
typedColorString = "Black";
// Error "Type 'string' is not assignable ..." (indexing works runtime)
let letColorString = "Red";
typedColorString = letColorString;
// Works fine
typedColorString = "Red";
// Works fine
const constColorString = "Red";
typedColorString = constColorString
// Works fine (thanks @SergeyT)
let letColorString = "Red";
typedColorString = letColorString as keyof typeof Color;
typedColor = Color[typedColorString];
https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types
TypeScript 2.4
Now has string enums so your code just works:
enum E {
hello = "hello",
world = "world"
};
TypeScript 1.8
Since TypeScript 1.8 you can use string literal types to provide a reliable and safe experience for named string values (which is partially what enums are used for).
type Options = "hello" | "world";
var foo: Options;
foo = "hello"; // Okay
foo = "asdf"; // Error!
More : https://www.typescriptlang.org/docs/handbook/advanced-types.html#string-literal-types
Legacy Support
Enums in TypeScript are number based.
You can use a class with static members though:
class E
{
static hello = "hello";
static world = "world";
}
You could go plain as well:
var E = {
hello: "hello",
world: "world"
}
Update:
Based on the requirement to be able to do something like var test:E = E.hello; the following satisfies this:
class E
{
// boilerplate
constructor(public value:string){
}
toString(){
return this.value;
}
// values
static hello = new E("hello");
static world = new E("world");
}
// Sample usage:
var first:E = E.hello;
var second:E = E.world;
var third:E = E.hello;
console.log("First value is: "+ first);
console.log(first===third);
In latest version (1.0RC) of TypeScript, you can use enums like this:
enum States {
New,
Active,
Disabled
}
// this will show message '0' which is number representation of enum member
alert(States.Active);
// this will show message 'Disabled' as string representation of enum member
alert(States[States.Disabled]);
Update 1
To get number value of enum member from string value, you can use this:
var str = "Active";
// this will show message '1'
alert(States[str]);
Update 2
In latest TypeScript 2.4, there was introduced string enums, like this:
enum ActionType {
AddUser = "ADD_USER",
DeleteUser = "DELETE_USER",
RenameUser = "RENAME_USER",
// Aliases
RemoveUser = DeleteUser,
}
For more info about TypeScript 2.4, read blog on MSDN.
I'm using framer motion and the typical little string typos are reminding me that maybe the time spent defining string enums is worth it.
Is there a way to use something like Object.keys to automatically generate a string enum with an object's keys? That would speed and clean things up quite a bit.
I don't think you can create enums like that, but you can have a type that is all the keys defined in an interface:
type K = keyof Interface;
I personally avoid enums at all costs. It's one of the only miss-steps in the design of the language, evidenced by the team not adding in any non-native runtime structures since.
Enums can be completely replaced by a union of known strings and/or a const object.
|undefineddoes not make a property optional, just means it can beundefined, there is a proposal to make|undefinedmembers optional but currently it's not implemented. You need to use?after]to make all properties optional{ [key in DialogType]?: Dialog }You can use the dialog enum values as keys, but they need to be computed properties:
let openDialogs: { [key in DialogType]?: Dialog } = { [DialogType.Options]: undefined, };{ [key: number or string]: Dialog }is an index signature. Index signatures are restricted to onlynumberorstringas the key type (not even a union of the two will work). So if you use an index signature you can index by anynumberorstring(we can't restrict to onlyDialogTypekeys). The concept you are using here is called mapped types. Mapped types basically generate a new type based on a union of keys (in this case the members of DialogType enum) and a set of mapping rules. The type we created above is basically equivalent to:let o: { [DialogType.Help]?: Dialog; [DialogType.Options]?: Dialog; }
Here is an example of how to use enum as index in typescript:
enum DialogType {
Options,
Help,
About
}
type Dialog = {
whatever: string;
};
type DialogMap = { [key in DialogType]?: Dialog };
let o: DialogMap = {
[ DialogType.Options ]: { whatever: "1" },
[ DialogType.Help ]: { whatever: "2" }
};