Amol Gupta
Amol Gupta

Reputation: 2554

Extending Enum in typescript

I was hoping to reuse certain values in enum. Any suggestions of how to achieve such functionality.

enum someEnum {
    a = 'Some String',
    b = 2,
};

enum extendedEnum {
    c = 'string',
    b = someEnum.b
}

type someEnumType<T extends someEnum> = T extends someEnum.a ? string :
    T extends someEnum.b ? number : never;

type extendedEnumType<T extends extendedEnum> =
    T extends extendedEnum.c ? string:          // Gives Error
    T extends extendedEnum.b ? number : never;  // Gives Error

Upvotes: 130

Views: 142298

Answers (4)

J Brewer
J Brewer

Reputation: 501

I've just stumbled across this post from 2018 that explains how to do it with string based enums (see the original comment).

Here's an annotated / fruity version:

enum someEnum {
  Apple = 'Apple',
  Banana = 'Banana'
}

enum extendedEnum {
  Pear = 'Pear',
  Grape = 'Grape'
}

// The key seems to be to declare a type AND
// a const with the same name

type AllFruits = someEnum | extendedEnum;
const AllFruits = {...someEnum, ...extendedEnum};

let f: AllFruits = AllFruits.Grape;

The original poster (who, by all rights, seems to have been a contributor to TypeScript and knows what they're talking about) mentions that, using this method, you can't use something like AllFruits.Grape as a 'type literal', which means you can't do this:

// This error will appear: 
// 'AllFruits' only refers to a type, but is being used as a namespace here.

interface FruitBowl {
    fruit: AllFruits.Grape    
}

but this can be fixed with (something quite ugly) like:

interface FruitBowl {
    fruit: typeof AllFruits.Grape    
}

I guess this is one of the 'type workarounds' that others have mentioned.

(All credit to https://github.com/alangpierce)

Upvotes: 46

Eric
Eric

Reputation: 575

Union enum does not give type intellisense, but 'as const' object literal does.

const Color1 = {
    Red : "Red",
    Green : "Green"
} as const

const Color2 = {
    Yellow : "Yellow",
    Blue : "Blue"
} as const

const Colors = {...Color1, ...Color2} as const

type ValueOf<T> = T[keyof T];
type ColorsType = ValueOf<typeof Colors>

const c:ColorsType = Colors.Green  // use 'as const'

alert(c)

Try it here

Upvotes: 15

William Moore
William Moore

Reputation: 3994

You could use a union in your type.

This works well for the task at hand. It allows to restrict x property to be only of values of abc or def enums. At the same time it allows using both enums as values.

enum abc {
    a = 1,
    b = 2,
    c = 3
}

enum def {
    d = 4,
    e = 5,
    f = 6
}

type abcdef = abc | def;

let x: abcdef;

x = abc.a;  // Here we use enum as value
x = def.d;  // As well here.

Upvotes: 21

Abolfazl Roshanzamir
Abolfazl Roshanzamir

Reputation: 14842

Currently, You can't extend enum in TypeScript

Another option is to use type:

enum Color1 {
    Red = "Red",
    Green = "Green"
}

enum Color2 {
    Yellow = "Yellow",
    Blue = "Blue"
}

define a new type named Colors :

type Colors = Color1 | Color2;

Then you can use it as below :

class AppComponent {
    public color: Colors;
    
    ngOnInit(): void {
      const Colors = { ...Color2, ...Color1 };
      this.color = Colors.Red; // Colors.Green or Colors.Yellow or Colors.Blue
    }
}

Stackblitz Here (Angular)

Stackblitz Here (Typescript)

Upvotes: 95

Related Questions