In TypeScript, the as const assertion is a powerful feature that tells the compiler to treat a value with the most specific and immutable type possible.
By default, TypeScript widens literal values. For example:
const role = "admin";
// role: stringBut with as const:
const role = "admin" as const;
// role: "admin"Now, role is of type "admin" instead of string, and it’s also readonly, which prevents accidental mutations.
Using as const can dramatically improve type safety, code predictability, and developer tooling (like autocomplete). It is especially useful when working with:
- Constant values
- Discriminated unions
- JSON-like config objects
- Literal arrays and tuples
Let’s dive deeper into each use case.
1- Literal Type Inference
Without as const:
const directions = ["up", "down", "left", "right"];
// directions: string[]With as const:
const directions = ["up", "down", "left", "right"] as const;
// directions: readonly ["up", "down", "left", "right"]Now, directions[0] is "up" instead of string, and the array is immutable (readonly).
This is especially useful when creating strict enums or options.
2. Discriminated Unions
In strongly-typed state machines or Redux reducers, discriminated unions are gold. as const makes them easy.
I wrote an article about Discriminated Unions, please check it out here.
const loginAction = {
type: "LOGIN",
payload: { userId: 1 },
} as const;
// loginAction.type: "LOGIN"Then you can define:
type Action =
| { type: "LOGIN"; payload: { userId: number } }
| { type: "LOGOUT" };
function reducer(state: any, action: Action) {
switch (action.type) {
case "LOGIN":
return { ...state, user: action.payload.userId };
case "LOGOUT":
return { ...state, user: null };
}
}Thanks to as const, TypeScript infers the type "LOGIN" automatically.
3. Constant Configuration Objects
When working with static configurations like API routes, role maps, or feature flags, as const makes the values readonly and exact:
const config = {
baseUrl: "https://api.example.com",
timeout: 5000,
retry: false,
} as const;TypeScript now infers:
{
readonly baseUrl: "https://api.example.com";
readonly timeout: 5000;
readonly retry: false;
}This prevents accidental reassignment and improves autocomplete.
4. Tuple Types and Function Overloads
If you’re using tuple types in your TypeScript utilities or writing function overloads, as const can help preserve the literal types of tuples.
function logPosition(pos: readonly [number, number]) {
console.log(`X: ${pos[0]}, Y: ${pos[1]}`);
}
const position = [10, 20] as const; // postion: [10, 20]
logPosition(position);Without as const, position would be typed as number[], not [10, 20].
You might wonder: what’s the difference between as const and readonly?
readonlyapplies to class properties or interface fields.as constis a runtime assertion that applies literal immutability to variables or expressions.
const myArr1: readonly number[] = [1, 2, 3];
const myArr2 = [1, 2, 3] as const;myArr1: readonly number[]
myArr2: readonly [1, 2, 3] (more specific)
In a Node.js app, you can use as const to define constants for:
- HTTP method literals:
const METHODS = ["GET", "POST", "PUT", "DELETE"] as const;
type Method = typeof METHODS[number]; // "GET" | "POST" | "PUT" | "DELETE"- Role-based access control:
const ROLES = {
admin: "ADMIN",
user: "USER",
guest: "GUEST",
} as const;
type Role = typeof ROLES[keyof typeof ROLES]; // "ADMIN" | "USER" | "GUEST"This guarantees that your roles and methods are exact and immutable, reducing runtime bugs and making your app more robust.
The as const assertion might seem small, but it can have a big impact on the clarity, safety, and expressiveness of your TypeScript code, especially in large projects where data structures need to be stable and predictable.
Want more dev insights like this? Subscribe to get practical tips, tutorials, and tech deep dives delivered to your inbox. No spam, unsubscribe anytime.


