TypeScript has added the erasableSyntaxOnly
flag, that doesn't let you use Enums.
Have looked for several alternative options. Like:
What's the best practice to implement an alternative to TypeScript `enum`?
https://maxheiber.medium.com/alternatives-to-typescript-enums-50e4c16600b1
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:
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
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