1 minute read

Enums exist. They’re not evil. But if you’ve written a TypeScript enum, you’ve already lost the argument. Take a look a the following example:

// This looks fine. It's not.
enum Language {
  CSharp = 'csharp',
  Python = 'python',
  PHP = 'php'
}

function checkLanguage(language: Language) { /* ... */ }

checkLanguage((Language as any).CSharp) //This compiles
The type Language is not the union ‘csharp’ ‘python’ ‘php’. It’s an enum type, a different thing entirely. TypeScript coerces between them silently, which means:
  • There is no static guarantee that callers pass enum members.

You lose the union type’s safety on every boundary: API response, JSON parsing, database row, function parameter. The enum type and the string union are two different types that TypeScript lets you mix for free.

Enums are strings in costumes

An enum member is a string under the hood. At runtime, Language.PHP is ‘php’. The only guarantee you have that callers pass enum members is discipline. The type system gave you nothing.

The real enum pattern in typeScript

const Language = {
  CSharp: 'csharp',
  Python: 'python',
  PHP: 'php'
} as const

// Derived union type — the real enum
type Language = (typeof Language)[keyof typeof Language];

function checkLanguage(language: Language) { /* ... */ }

checkLanguage('chsarp'); // Error
checkLanguage(Language.CSharp); // Works
checkLanguage('csharp'); // Works

Why this beats enum

  • No type coercion hole. The derived union is exactly the values. A string literal that isn’t a value fails to type-check, period.
  • No in-check trickery. Values are strings, keys are strings. Your data boundary checks out naturally against the union.
  • No numeric index map. Object.values(RolesValues) gives you the actual values array.
  • String-safe JSON. Serialize/deserialize and validate at the boundary — same pattern every time.

The cost almost nothing. The gain is that typescript type system is working for you, not the other way around.

Conclusion

Enums in TypeScript are strings with a type system hole. Union types give you the safety you actually want, with less runtime machinery and fewer surprises at every data boundary.

Happy coding!