Using enum as interface key in typescript

TypescriptEnums

Typescript Problem Overview


I was wonder if I can use enum as an object key in interfaces.. I've built a little test for it:

export enum colorsEnum{
red,blue,green
}

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

When I run it I'm getting the following error:

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

I'm doing it wrong or it just not possible?

Typescript Solutions


Solution 1 - Typescript

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
};

Solution 2 - Typescript

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.

Solution 3 - Typescript

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.

Solution 4 - Typescript

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;
}

Solution 5 - Typescript

This worked for us:

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

Solution 6 - Typescript

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
}

Solution 7 - Typescript

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',
};

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGili YanivView Question on Stackoverflow
Solution 1 - TypescriptYan QView Answer on Stackoverflow
Solution 2 - TypescriptNisim JosephView Answer on Stackoverflow
Solution 3 - TypescriptFentonView Answer on Stackoverflow
Solution 4 - TypescriptMetal SaninView Answer on Stackoverflow
Solution 5 - TypescriptwestandyView Answer on Stackoverflow
Solution 6 - TypescriptMarekView Answer on Stackoverflow
Solution 7 - TypescriptEduardo CuomoView Answer on Stackoverflow