Introduction
Need a reliable way to attach labels, icons, or colors to an enum without scattering switch statements? Record gives you a single, type-safe map so UI metadata stays centralized, predictable, and easy to extend. This guide shows how it works with both union types and real-world enums.
What is Record in TypeScript?
Record<K, T> creates an object type whose keys must be of type K and whose values must be of type T. It is perfect for mapping a fixed set of keys to structured metadata.
// Union type of possible roles
type UserRole = "admin" | "user" | "guest";
// Keys must be each role; values must be strings
const roles: Record<UserRole, string> = {
admin: "Administrator",
user: "Regular User",
guest: "Guest User",
};
// Safe lookup
console.log(roles.admin); // "Administrator"
// Error: TypeScript prevents unknown keys
// roles.superAdmin = "Super Admin";
This pattern guarantees full coverage of all keys and prevents accidental extras.
Using Record with enums (UI metadata)
Enums show up everywhere in production apps. Suppose you need labels and colors for payment statuses:
enum PaymentStatus {
Unpaid = 0,
Paid = 1,
Failed = 2,
}
Instead of repeating switch statements, create one source of truth:
const paymentStatusMeta: Record<
PaymentStatus,
{ label: string; color: string }
> = {
[PaymentStatus.Unpaid]: { label: "Unpaid", color: "orange" },
[PaymentStatus.Paid]: { label: "Paid", color: "green" },
[PaymentStatus.Failed]: { label: "Failed", color: "red" },
};
Now you can render metadata directly:
const status = PaymentStatus.Paid;
console.log(paymentStatusMeta[status].label); // "Paid"
console.log(paymentStatusMeta[status].color); // "green"
Helper function for cleaner usage
Wrap lookups to keep call sites expressive and future-proof:
function getPaymentStatusMeta(status: PaymentStatus) {
return paymentStatusMeta[status];
}
// Usage
const info = getPaymentStatusMeta(PaymentStatus.Failed);
console.log(info.label); // "Failed"
console.log(info.color); // "red"
If you later add localization or formatting, you update the function without touching the rest of your codebase.
Why prefer Record over switch/case?
- Full coverage enforced: TypeScript requires every enum or union key to exist in the map.
- Strong typing: Values must match the declared shape, preventing accidental mismatches.
- Centralized metadata: Labels, colors, and icons live in one place.
- Cleaner reads:
meta[status].labelis easier to scan than repeated switch statements. - Safer extensions: Adding a new enum value forces you to supply its metadata at compile time.
Summary
Record<K, T>builds a type-safe map from known keys to structured values.- Use it to store UI metadata for enums or union types instead of scattered switches.
- Wrap lookups in a helper to add localization or formatting later without refactors.
- The compiler enforces full coverage and correct value shapes, reducing runtime bugs.
Conclusion
Record keeps enum metadata centralized, readable, and type-safe. Use it as the single source of truth for anything your UI needs to display for a given status or role, and let TypeScript enforce completeness for you. Happy coding!