Use as const in TypeScript

The as const assertion tells TypeScript to treat a value as fully immutable with literal types. Learn when to use it, what it does to objects and arrays, and how it enables enum-like patterns.

5 min read

The as const assertion is a TypeScript feature that tells the compiler to treat a literal value as completely immutable and to infer the narrowest possible types. It was introduced in TypeScript 3.4 and has become the standard way to create deeply typed constant data.

With as const, string values get literal types, numbers get numeric literal types, object properties become readonly, and arrays become readonly tuples. A single keyword does all of this at once.

typescripttypescript
const theme = "dark" as const;
// Type: "dark"
 
const sizes = ["small", "medium", "large"] as const;
// Type: readonly ["small", "medium", "large"]
 
const config = { api: "v2", port: 3000 } as const;
// Type: { readonly api: "v2"; readonly port: 3000 }

Each value keeps its exact type. The string is not just a string, it is that specific string. The array is not just a string array, it is a fixed-length tuple with known elements.

The object properties cannot be reassigned.

What As Const Does

The as const assertion does three things to any literal expression. First, it prevents type widening so every value keeps its literal type instead of widening to a general type. Second, it marks all object properties as readonly.

Third, it converts array literals to readonly tuples with literal element types.

These changes are deep. Every nested object and array inside the expression inherits the same treatment. A single assertion at the top level flows through the entire value tree, which means you do not need to add readonly or literal annotations at each level.

The assertion only works on literal expressions. You cannot apply it to computed values, function return values, or variables that hold runtime-determined data. The compiler must be able to evaluate the expression at compile time to infer the literal types.

Objects with As Const

The most common use of as const is on configuration objects. Without it, TypeScript infers general types that allow mutation and lose the specific values.

typescripttypescript
const settings = {
  theme: "dark",
  version: 2,
} as const;
 
settings.theme = "light";
// Error: Cannot assign to 'theme' because it is a read-only property

Every property is now readonly and has a literal type. The compiler rejects any attempt to reassign a property. This catches accidental mutations in configuration that should be fixed for the lifetime of the application.

For more on how readonly works independently of as const, see Readonly values in TypeScript.

Arrays with As Const

Arrays with as const become readonly tuples. Each element keeps its literal type, and all mutating methods are blocked. This is useful when you need a fixed sequence of values that should never change.

typescripttypescript
const directions = ["north", "south", "east", "west"] as const;
// Type: readonly ["north", "south", "east", "west"]
 
directions.push("up");
// Error: Property 'push' does not exist

You can derive a union type from the array, avoiding duplication. The type expression extracts each element's literal type and combines them into a union. This keeps the array as the single source of truth for both runtime and compile-time usage.

typescripttypescript
type Direction = (typeof directions)[number];
// Type: "north" | "south" | "east" | "west"

This pattern is widely used for string constants, HTTP methods, status values, and any set of known options that need both a runtime array and a compile-time type.

Enum-Like Patterns

As const can replace TypeScript enums in many cases. An as const object provides named constants with exact literal types, without the runtime overhead of TypeScript's enum feature.

typescripttypescript
const Status = {
  Active: "ACTIVE",
  Inactive: "INACTIVE",
  Pending: "PENDING",
} as const;
 
type StatusType = (typeof Status)[keyof typeof Status];
// Type: "ACTIVE" | "INACTIVE" | "PENDING"

This approach is lighter than enums because it does not generate extra JavaScript. It also integrates naturally with APIs that expect string values, since each value is already a plain string. For more on literal types, see TypeScript literal types explained.

As Const Limitations

As const only applies to literal expressions. You cannot use it on computed expressions because the compiler needs to know the exact value at compile time. A ternary expression or an arithmetic result cannot be asserted with as const.

It also does not deeply freeze at runtime. It is a type-level only directive. The JavaScript output is unchanged, so if another piece of code has a reference to the same object without the readonly type, it can still mutate the data.

The protection is at the type-checking level, not at the JavaScript runtime level.

Nested mutable references inside an as const object are still readonly at their reference level, but the values they point to might be mutable if they are shared references. This is the same behavior as readonly properties in general.

When to Use As Const

Use as const on configuration objects that should never change during the application's life. Use it on arrays of known values when you need both runtime iteration and compile-time type unions. Use it on string constants to create enum-like patterns without generating extra JavaScript.

Skip as const on data that is intentionally mutable. Do not use it on API responses, user input, or any value that comes from an external source. The assertion is for values that you define and control at compile time.

For how as const relates to the wider type inference system, see Type widening in TypeScript.

Rune AI

Rune AI

Key Insights

  • as const locks an object or array into its most specific literal type.
  • All properties become readonly; arrays become readonly tuples.
  • It only works on literal expressions, not computed values.
  • Use as const for configuration, string constants, and enum-like patterns.
  • No runtime effect: as const is erased during compilation.
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between const and as const?

const prevents reassignment of a variable. as const makes every property readonly and infers literal types for every value. const is for the variable binding; as const is for the type of the value itself.

Does as const affect runtime behavior?

No. as const is a compile-time only directive. It is erased during compilation and has zero effect on the JavaScript output.

Can I use as const on a let variable?

Yes. as const works on any literal expression, whether assigned to let or const. But even with as const, a let variable can still be reassigned to a different value.

Conclusion

as const is the simplest way to tell TypeScript that a value is truly fixed. It infers the narrowest possible literal types, marks every property as readonly, and turns arrays into readonly tuples. Use it on configuration objects, string constants, and any data structure that should not change. It has zero runtime cost and makes your types more precise with a single keyword.