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
readonly
properties - Array literals become
readonly
tuples
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 perfectly
The 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 delete
3. 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 Status
const status = "success"; // Type: string
showStatus(status); // Error
const status = "success" as const; // Type: "success"
showStatus(status); // Works
How 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 method
Common 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 tuple
Limitations 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.