as const in TypeScript: Const Assertions Explained
as const is a TypeScript feature known as a const assertion that transforms how TypeScript infers types for your values, making them more specific and immutable.
What Does as const Do?
When you use as const, TypeScript infers the narrowest possible type for an expression. This means:
- Literal types are not widened (e.g.,
"success"stays as literal type"success"instead of being widened tostring) - Object literals get
readonlyproperties - Array literals become
readonlytuples
Key Benefits
1. Tuple Type Safety
Consider the original example:
// Without as const: args is inferred as number[]
const args = [8, 5];
const angle = Math.atan2(...args); // Error: Expected 2 arguments// With as const: args is readonly [8, 5]
const args = [8, 5] as const;
const angle = Math.atan2(...args); // Works perfectlyThe as const assertion tells TypeScript that args is a tuple with exactly two numbers in specific positions.
2. Immutable Data Structures
const config = {
host: "localhost",
port: 8080,
ssl: true
} as const;
// All these will cause compilation errors:
config.host = "example.com"; // ❌ Cannot assign to 'host'
config.port = 3000; // ❌ Cannot assign to 'port'
delete config.ssl; // ❌ Cannot delete3. Preventing Type Widening
type Status = "success" | "danger";
function showStatus(status: Status) {
console.log(status);
}
const status = "success"; // Type is string, not "success"
showStatus(status); // ❌ Error: string not assignable to Statusconst status = "success"; // Type: string
showStatus(status); // Errorconst status = "success" as const; // Type: "success"
showStatus(status); // WorksHow It Differs From Regular const
Regular const only prevents variable reassignment, not mutation of the referenced object:
const regularArray = [1, 2, 3];
regularArray[0] = 999; // ✅ Allowed
regularArray.push(4); // ✅ Allowed
const constArray = [1, 2, 3] as const;
constArray[0] = 999; // ❌ Error: readonly
constArray.push(4); // ❌ Error: no push methodCommon Use Cases
1. Configuration Objects
const APP_CONFIG = {
version: "1.0.0",
apiUrl: "https://api.example.com",
retryAttempts: 3,
timeout: 5000
} as const;
// Type is inferred as:
// {
// readonly version: "1.0.0";
// readonly apiUrl: "https://api.example.com";
// readonly retryAttempts: 3;
// readonly timeout: 5000;
// }2. Redux/Action Creators
const setUser = (user: User) => ({
type: "SET_USER" as const,
payload: user
});
// Type is properly narrowed to:
// { readonly type: "SET_USER"; readonly payload: User; }3. Function Parameters
function calculateArea(dimensions: readonly [number, number]) {
return dimensions[0] * dimensions[1];
}
const dims = [10, 20] as const;
calculateArea(dims); // ✅ Works with readonly tupleLimitations and Considerations
WARNING
as const is a compile-time only feature. At runtime, JavaScript objects remain mutable - TypeScript only prevents you from writing code that would mutate them.
INFO
There are some edge cases where as const doesn't make everything completely readonly. Check the TypeScript documentation for details.
When to Use as const
- When you need to pass literal values to functions expecting specific types
- When creating configuration objects that shouldn't be modified
- When working with tuples that have fixed lengths and types
- When you want to prevent accidental mutations in your codebase
Conclusion
as const is a powerful TypeScript feature that enables more precise type inference and immutability at the type level. It helps catch bugs at compile time by making your intentions explicit to the TypeScript compiler, leading to more robust and maintainable code.