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: string
But 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
?
readonly
applies to class properties or interface fields.as const
is 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.