Enums, Tuples & Special Types
📖 Concept
Enums define a set of named constants. TypeScript has numeric enums, string enums, and const enums.
Tuples are fixed-length arrays where each position has a specific type. They're like arrays with a known structure.
Special types round out TypeScript's type system — as const, assertion functions, branded types, and more.
Enum debate: Many TypeScript experts (including the TS team) now recommend union literal types or as const objects over enums because:
- Enums generate runtime JavaScript code (they're not type-only)
const enumis fragile across module boundaries- Union literals are zero-cost and tree-shakeable
Tuples are critical for:
- React hooks:
useStatereturns[T, SetterFn] - Coordinate data:
[number, number] - Function overloads with variadic args
- Named tuples for readability:
[name: string, age: number]
as const creates the narrowest possible type — all properties become readonly and values become literal types. This is how you create type-safe constant objects.
🏠 Real-world analogy: An enum is like a menu with numbered items — "1. Coffee, 2. Tea, 3. Water". as const is like a menu printed on a brass plaque — it's fixed, permanent, and everyone sees exactly the same options.
💻 Code Example
1// Numeric Enum (auto-increments from 0)2enum Direction {3 Up, // 04 Down, // 15 Left, // 26 Right // 37}8console.log(Direction.Up); // 09console.log(Direction[0]); // "Up" (reverse mapping)1011// String Enum (recommended over numeric)12enum Status {13 Active = "ACTIVE",14 Inactive = "INACTIVE",15 Pending = "PENDING"16}1718// ⚠️ Enums generate runtime code:19// var Status;20// (function (Status) {21// Status["Active"] = "ACTIVE";22// ...23// })(Status || (Status = {}));2425// ✅ Better alternative: union literal types (zero-cost)26type StatusType = "ACTIVE" | "INACTIVE" | "PENDING";2728// ✅ Or: as const object (runtime values + type safety)29const STATUS = {30 Active: "ACTIVE",31 Inactive: "INACTIVE",32 Pending: "PENDING"33} as const;34type StatusValue = typeof STATUS[keyof typeof STATUS];35// "ACTIVE" | "INACTIVE" | "PENDING"3637// Tuples — fixed length, typed positions38let point: [number, number] = [10, 20];39let entry: [string, number] = ["age", 30];4041// Named tuples (TypeScript 4.0+) — for readability42type UserTuple = [name: string, age: number, active: boolean];43const user: UserTuple = ["Alice", 30, true];4445// Rest elements in tuples46type StringAndNumbers = [string, ...number[]];47const data: StringAndNumbers = ["scores", 90, 85, 95];4849// React useState pattern50function useState<T>(initial: T): [T, (value: T) => void] {51 let state = initial;52 const setState = (value: T) => { state = value; };53 return [state, setState];54}55const [count, setCount] = useState(0); // Properly typed!5657// as const — creates the narrowest type58const CONFIG = {59 api: "https://api.example.com",60 timeout: 5000,61 retries: 3,62 methods: ["GET", "POST"] // readonly ["GET", "POST"], not string[]63} as const;64// CONFIG.timeout = 10000; // ❌ Error: readonly65// type: { readonly api: "https://..."; readonly timeout: 5000; ... }6667// Const assertions with arrays68const ROLES = ["admin", "editor", "viewer"] as const;69type Role = typeof ROLES[number]; // "admin" | "editor" | "viewer"7071// Assertion functions72function assertIsString(value: unknown): asserts value is string {73 if (typeof value !== "string") {74 throw new Error(`Expected string, got ${typeof value}`);75 }76}77function process(input: unknown) {78 assertIsString(input);79 console.log(input.toUpperCase()); // TS knows input is string!80}
🏋️ Practice Exercise
Mini Exercise:
- Create both an enum and an
as constobject for roles — compare the compiled JavaScript output - Declare a tuple type for a database row:
[id: number, name: string, email: string, active: boolean] - Use
as constto create a readonly config object and derive a union type from its values - Write an assertion function
assertNonNull<T>(value: T | null): asserts value is T - Use rest elements in a tuple:
[first: string, ...rest: number[]]
⚠️ Common Mistakes
Using numeric enums with
0as a value —0is falsy, leading to bugs likeif (role)failing for the first enum memberNot knowing enums generate runtime JavaScript — unlike types/interfaces, enums are NOT erased at compile time
Using
const enumacross module boundaries — it can cause issues with declaration files and some bundlers (e.g., isolatedModules)Thinking tuples can have any length — tuples have a FIXED length; TypeScript will error if you try to access beyond the declared positions
Forgetting
as conston object literals — without it,{ role: 'admin' }has type{ role: string }instead of{ role: 'admin' }
💼 Interview Questions
🎤 Mock Interview
Practice a live interview for Enums, Tuples & Special Types