Typescript: string literal union type from enum

TypescriptEnums

Typescript Problem Overview


I'd like to get a string literal union from an enum.

For this enum…

enum Weekday {
    MONDAY = 'mon',
    TUESDAY = 'tue',
    WEDNESDAY = 'wed'
}

… I'd like to get this:

type WeekdayType = 'mon' | 'tue' | 'wed';

I tried typeof keyof Weekday but that resulted in 'MONDAY' | 'TUESDAY' | 'WEDNESDAY'. Feel like the solution might have to do with mapped types but I can't seem to wrap my head around it.

How do I do this?

Typescript Solutions


Solution 1 - Typescript

See TS4.1 ANSWER:

type WeekdayType = `${Weekday}`;

PRE TS-4.1 ANSWER:

This can't be done programmatically... you're trying to convert the type Weekday, which is Weekday.MONDAY | Weekday.TUESDAY | Weekday.WEDNESDAY, to the type WeekdayType which is "mon" | "tue" | "wed". This conversion is a form of widening, since Weekday is a subtype of WeekdayType:

type WeekdayExtendsWeekdayType = 
  Weekday extends WeekdayType ? true : false
// type WeekdayExtendsWeekdayType = true

Unfortunately the compiler doesn't give you a handle to remove an "enum"-ness from the enum type and leave you with plain literal types.


So, workarounds? Maybe you don't actually need an enum, but can make do with an object whose property values are string literals:

const lit = <V extends keyof any>(v: V) => v;
const Weekday = {
  MONDAY: lit("mon"),
  TUESDAY: lit("tue"),
  WEDNESDAY: lit("wed")
}
type Weekday = (typeof Weekday)[keyof typeof Weekday],

If you inspect it, the value named Weekday behaves like an enum object:

console.log(Weekday.TUESDAY); // tue

while the type named Weekday behaves like the union of string values "mon" | "tue" | "wed" that you were calling WeekdayType:

const w: Weekday = "wed"; // okay
const x: Weekday = "xed"; // error

So in this workaround, there is no "enum"-ness, and therefore no need to distinguish the type Weekday from the type WeekdayType. It's a little different from an actual enum (which includes types like Weekday.MONDAY, which you'd have to represent as the cumbersome typeof Weekday.MONDAY or create a different type alias for it), but it might behave similarly enough to be useful. Does that work for you?

Hope that helps. Good luck!

Solution 2 - Typescript

TypeScript 4.1+:

As mentioned, this can be achieved by using Template Literal Types like so:

type WeekdayType = `${Weekday}`;

TypeScript 3.4+:

Following up on @jcalz answer and the comment from @just-boris, here's an example with const assertions:

const Weekday = {
  MONDAY: "mon",
  TUESDAY: "tue",
  WEDNESDAY: "wed",
} as const;

type Weekday = (typeof Weekday)[keyof typeof Weekday];

Edit:

Wrote a blog post for those who would like to dig deeper.

Solution 3 - Typescript

With Typescript 4.1, it can be done!

enum Weekday {
  MONDAY = 'mon',
  TUESDAY = 'tue',
  WEDNESDAY = 'wed'
}

type WeekdayType = `${Weekday}`;

Caveat: This only works with string enum values, not number enum values.

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
QuestionAKGView Question on Stackoverflow
Solution 1 - TypescriptjcalzView Answer on Stackoverflow
Solution 2 - TypescriptdesignciseView Answer on Stackoverflow
Solution 3 - Typescriptcoyotte508View Answer on Stackoverflow