Difficulty: Intermediate
What are literal types in TypeScript? How does 'as const' work and when should you use it?
Literal types represent exact values rather than broad categories. Instead of 'string', a literal type is a specific string like 'hello'. TypeScript automatically infers literal types for const declarations.
'as const' (const assertion) tells TypeScript to infer the narrowest possible type for an expression: - Strings/numbers become literal types instead of string/number - Arrays become readonly tuples instead of mutable arrays - Objects get readonly properties with literal types
This is incredibly useful for configuration objects, action types, and anywhere you want TypeScript to track exact values.
// String literal types
type Direction = 'north' | 'south' | 'east' | 'west';
let dir: Direction = 'north'; // OK
// dir = 'up'; // Error: Type '"up"' is not assignable to type 'Direction'
// Numeric literal types
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
function rollDice(): DiceRoll {
return (Math.floor(Math.random() * 6) + 1) as DiceRoll;
}
// Boolean literal types
type True = true;
type False = false;
// let vs const inference
let mutable = 'hello'; // type: string (wide)
const immutable = 'hello'; // type: 'hello' (literal)
// Literal types in function parameters
function setAlignment(align: 'left' | 'center' | 'right'): void {
document.body.style.textAlign = align;
}
setAlignment('center'); // OK
// setAlignment('top'); // Error
Literal types restrict values to exact matches. They are the foundation of discriminated unions and enable powerful pattern matching.
// Without as const
const config = {
endpoint: 'https://api.example.com',
retries: 3,
methods: ['GET', 'POST'],
};
// type: { endpoint: string; retries: number; methods: string[] }
// With as const
const configConst = {
endpoint: 'https://api.example.com',
retries: 3,
methods: ['GET', 'POST'],
} as const;
// type: {
// readonly endpoint: 'https://api.example.com';
// readonly retries: 3;
// readonly methods: readonly ['GET', 'POST'];
// }
// Useful for action types (Redux-style)
const ACTIONS = {
INCREMENT: 'INCREMENT',
DECREMENT: 'DECREMENT',
RESET: 'RESET',
} as const;
type ActionType = typeof ACTIONS[keyof typeof ACTIONS];
// type: 'INCREMENT' | 'DECREMENT' | 'RESET'
// Without as const, ActionType would just be 'string'
as const freezes values to their literal types and makes everything readonly. It's essential for deriving union types from objects.
// Route definitions
const ROUTES = {
home: '/',
about: '/about',
users: '/users',
userDetail: '/users/:id',
} as const;
type Route = typeof ROUTES[keyof typeof ROUTES];
// '/' | '/about' | '/users' | '/users/:id'
function navigate(route: Route): void {
window.location.href = route;
}
navigate(ROUTES.home); // OK
// navigate('/random'); // Error
// Tuple from as const
const rgb = [255, 128, 0] as const;
// type: readonly [255, 128, 0] (not number[])
// Enum-like pattern without enum keyword
const Status = {
Active: 'active',
Inactive: 'inactive',
Pending: 'pending',
} as const;
type Status = typeof Status[keyof typeof Status];
// 'active' | 'inactive' | 'pending'
function setStatus(status: Status): void {
console.log(`Setting status to: ${status}`);
}
setStatus(Status.Active); // OK
The 'as const + typeof + keyof' pattern is a popular alternative to enums. It provides the same type safety with less runtime overhead.
Literal Types, const Assertions, as const, Template Literals, Narrowing