Step 1 of 7 · Reading · ~4 min
Learn
Type System Essentials
Enums and Literal Types
Many values come from a small closed set: a size, a direction, a log level. TypeScript has two ways to represent such a set: literal-type unions (the modern default) and enums (the older feature). By the end of this lesson you can build a closed set from a single array, derive its type from that array, check untrusted text against it at runtime, and explain why most new code avoids enum.
String literal unions
type Size = 'small' | 'medium' | 'large';
function label(s: Size): string {
return `size: ${s}`;
}
label('medium'); // OK
label('huge'); // compile error: not a Size
At runtime these are ordinary strings. The union exists only for the checker and costs nothing in the output.
as const and one source of truth
By default TypeScript widens literal values to their general type:
const sizes = ['small', 'medium', 'large']; // string[]: the literals are lost
as const keeps every literal narrow and marks the value deeply read-only to the compiler:
const SIZES = ['small', 'medium', 'large'] as const; // readonly ['small', 'medium', 'large']
type Size = typeof SIZES[number]; // 'small' | 'medium' | 'large'
typeof SIZES is the array's type, and indexing a type with [number] asks "what type sits at any numeric index?", which is the union of the element types. The array is now the single source of truth: add 'huge' to it and the type follows. The same trick works on objects:
const COLORS = { red: '#f00', green: '#0f0' } as const;
type ColorName = keyof typeof COLORS; // 'red' | 'green'
keyof T is the union of the key names of the object type T, so keyof typeof COLORS lists the keys of COLORS.
Checking untrusted text at runtime
A union is a compile-time promise. Text that arrives from input is just a string until you check it, and the check is ordinary code:
function isSize(word: string): boolean {
return (SIZES as readonly string[]).includes(word);
}
The cast widens the read-only tuple to readonly string[] so that includes accepts any string. An array's order is also data you can use: positions can double as ranks, so a set that has a natural order, such as small to large, can express it by the order of its elements.
Numeric and boolean literal types
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
type Loading =
| { loading: true }
| { loading: false; data: string };
A number or boolean can be a type just like a string, and the second example is a small state machine whose loading field is the discriminant.
enum: the legacy approach
enum Color {
Red, // 0
Green, // 1
Blue // 2
}
enum HttpStatus { OK = 200, NotFound = 404 }
enum Direction { North = 'N', South = 'S' } // a string enum
console.log(Color.Green); // 1
Enums work, but they have costs:
- Every enum compiles to a real runtime object, which becomes part of your output.
- A numeric enum accepts any number:
const c: Color = 99compiles. const enuminlines values but has caveats across module boundaries and is disabled in many setups.- Two identical-looking enums declared in different places are different types.
The common modern replacement is a plain object with as const:
const Direction = { North: 'N', South: 'S' } as const;
type Direction = typeof Direction[keyof typeof Direction]; // 'N' | 'S'
You get a runtime object (Direction.North) and a union type with the same name, without the quirks.
Common mistakes
- Forgetting
as const. The array widens tostring[], so no union can be derived from it. - Treating the union as a runtime check. Text from input must still be tested with ordinary code before you rely on it.
- Reaching for
enumby reflex. A literal union is usually simpler and leaves nothing in the output. - Assuming
as constfreezes the value. It is a compile-time rule; useObject.freezeif you need a runtime guarantee.
Your exercise
Filter log lines by severity. The levels form a closed, ordered set, so one as const array can provide the type, the runtime check and the ordering. You declare it and write a function that turns level text into a rank. The starter reads the lines and leaves the filtering decision to you. The hidden tests include lines whose level is not in the set, and the minimum level itself.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…