100

I would like to know if it is possible to assign an enum as a key to an object in an interface?

I wrote the snippet bellow to test if is (or isn't) possible.

    export enum colorsEnum{
    red,blue,green
    }
    
    export interface colorsInterface{
    [colorsEnum.red]:boolean,
    [colorsEnum.blue]:boolean,
    [colorsEnum.green]:boolean
    }

However, When I run the snippet, an error occurs that prints the following message:

A computed property name in an interface must directly refer to a built-in symbol.

I'm doing it wrong, or is it simply just not possible?


7 Answers 7

128

You can try with type:

export enum colorsEnum{
    red, blue, green
}

export type colorsInterface = {
    [key in colorsEnum]: boolean;
};

let example: colorsInterface = {
    [colorsEnum.red]: true,
    [colorsEnum.blue]: false,
    [colorsEnum.green]: true
};

Or if you do not want to use all keys: add a ?

export type colorsInterface = {
    [key in colorsEnum]?: boolean;
};

let example: colorsInterface = {
    [colorsEnum.red]: true,
    [colorsEnum.blue]: false
};
Sign up to request clarification or add additional context in comments.

4 Comments

wow, this even works with string enums export enum ColorsEnum{ red = "red", blue = "blue", green = "green", }
This is essentially what the typescript Record utility type does for you. typescriptlang.org/docs/handbook/… stackoverflow.com/questions/51936369/…
This should be marked as the correct answer.
This is just the solution I was looking for. Thank you!
41

OK, the key idea is to convert the Enum to the correct Type and to extends the Interface with it: You can check it out in live code here.

const enum Enum {
    key1 = "value1",
    key2 = "value2",
    key3 = "value3",
}
type EnumKeys = keyof typeof Enum;
type EnumKeyFields = {[key in EnumKeys]:boolean}

interface IEnumExtended extends EnumKeyFields {
    KeyEx1:boolean;
    KeyEx2:string;
}

// Test it
const enumInstance: IEnumExtended = {

};

when you inside the enumInstance you will get autocomplete for the Enum keys and not the values.

5 Comments

marvellous, I was looking for the same. This should be the accepted answer.
what you earn from this line "type EnumKeys = keyof typeof Enum; " ?
hi @yehonatanyehezkel, I earn Order and I notice the TS compiler working better when I create the "type". when doing {[key in keyof typeof Enum]:boolean} the compiler complains and it not so understandable when the code is not split to 2 lines. plus, I want to use the Keys and not the Values. The values can be explanation of the keys.
Is it possible to extend this to have one enum for booleans and another for strings and combine them into a single field list type? Or perhaps specify the type as the value?
You can set type per Enum keys. and yes, you can extends multiple EnumKeyFields types as you want to the interface, each with other type, just make sure the keys are different.
24

A simple solution using the native Record<Keys, Type> utility. (Docs)

export enum Colors {
    RED = 'red',
    GREEN = 'green',
    BLUE = 'blue'
}

export type ColorInterface = Record<Colors, boolean>

This type translates to:

// translates to:

export type ColorInterface = {
    red: boolean;
    green: boolean;
    blue: boolean;
}

IMPORTANT: You must to define an enum key and map the values accordingly to them, else, you'll get a type / interface that uses an enum's index like the following:

export enum Colors {
    'red',
    'green',
    'blue'
}

export type ColorInterface = Record<Colors, boolean>

// translates to:

export type ColorInterface = {
    0: boolean;
    1: boolean;
    2: boolean;
}

Alternatively, you can also define the Colors using type alias if you don't want to explicitly define the enum keys or if you have just a few keys to use, this will also translate properly to what you need:

export type Colors = 'red' | 'green' | 'blue'

// will also translate to:

export type ColorInterface = {
    red: boolean;
    green: boolean;
    blue: boolean;
}

1 Comment

Easiest solution here!
23

To define an interface, the member names must be supplied not computed.

export interface colorsInterface {
    red: boolean;
    blue: boolean;
    green: boolean;
}

If you are worried about keeping the enum and the interface in sync you could use the following:

export interface colorsInterface {
    [color: number]: boolean;
}

var example: colorsInterface = {};
example[colorsEnum.red] = true;
example[colorsEnum.blue] = false;
example[colorsEnum.green] = true;

TypeScript is perfectly happy for you to pass the enum as the index and a rename-refactor would then keep everything together if you decided to rename red, for example.

1 Comment

This is ok but what if we would like to restrict key values (in the signature) down to the available keys of the enum rather than a color:number. The [color: key in colorsEnum] does exactly this but requires you to provide all enum keys when you create a new object of this interface... So in my case I need even stronger type checking but for a partial of the enums keys. For now, to support my use case I just falled back to using primitives in my signature :(
12

This worked for us:

type DictionaryFromEnum = {
  [key in keyof typeof SomeEnum]?: string
}

Comments

8

Why not to keep it as simple as it should be:

export enum Color {
    Red = 'red',
    Blue = 'blue',
    Green = 'green'
}

export interface IColors{
    [Color.Red]: boolean,
    [Color.Blue]: boolean,
    [Color.Green]: boolean
}

Comments

4

Probably you are searching for key remapping via as.

Note: Requires TypeScript ^4.1.

Example:

enum Color {
  red,
  blue,
  green,
}

// Define valid colors
// type TColor = 'red' | 'blue' | 'green';
type TColor = keyof typeof Color;

// Define object structure, with `color` as prefix for each `TColor`
type TWithColorCode = {
  [colorKey in TColor as `color${Capitalize<string & colorKey>}`]: string;
};

const a: TWithColorCode = {
  // All properties will be required
  colorGreen: '#00FF00',
  colorBlue: '#0000FF',
  colorRed: '#FF0000',
};

// Extending an `interface`:

export interface ICarRGB extends TWithColorCode {
  id: number;
  name: string;
  // Optional property
  createdAt?: Date;
}

const b: ICarRGB = {
  id: 1,
  name: 'Foo',
  colorGreen: '#00FF00',
  colorBlue: '#0000FF',
  colorRed: '#FF0000',
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.