typescriptenums

TypeScript Enum alternative following the use of --erasableSyntaxOnly flag


TypeScript has added the erasableSyntaxOnly flag, that doesn't let you use Enums.

Have looked for several alternative options. Like:

My best solution yet, for type safety and code completion is object literals with type-definition:

export const Environment = {
   Development: 'development',
   Production: 'production',
   Test: 'test',
}
export type Environment =   typeof  Environment[keyof typeof Environment];

But, it's not pretty and looks hacky, especially the last line:

  1. Why do we need to add this convoluted line, just to get the type?
  2. Is Environment a type? A value? Both? This is not clear and error prone.

Is there a recommended way to migrate away from Enums? Haven't seen one in the docs


Solution

  • If you require the enum type safety you could mimic that behavior by using branded types. You can define a helper function thats builds you an enum like runtime object which has values of branded strings. This way the values distinguish from string literals.

    type Branded<T, Brand extends string> = T & { __brand: Brand };
    const brand = "environment";
    
    const makeEnvironment = <const T extends Record<string, string>>(obj: T) => {
      return Object.fromEntries(
        Object.entries(obj).map(([k, v]) => [
          k,
          v as Branded<typeof v, typeof brand>,
        ]),
      ) as {
        [K in keyof T]: Branded<T[K], typeof brand>;
      };
    };
    
    const environment = makeEnvironment({
      Development: "development",
      Production: "production",
      Test: "test",
    });
    
    type Environment = (typeof environment)[keyof typeof environment];
    
    declare const test: (e: Environment) => void;
    
    test("development");
    //   ~~~~~~~~~~~~~ -> Argument of type '"development"'
    // is not assignable to parameter of type 'Environment'.
    test(environment.Development); // works fine
    

    TypeScript Playground