Advanced Patterns

as const assertion

The as const assertion marks a value as deeply readonly with the most specific literal types possible, preventing type widening.

Definition

TypeScript
const colors = ["red", "green", "blue"] as const;
// readonly ["red", "green", "blue"]

Examples

Literal Tuple
const colors = ["red", "green", "blue"] as const;
// readonly ["red", "green", "blue"]

type Color = (typeof colors)[number];
// "red" | "green" | "blue"

function isColor(value: string): value is Color {
  return (colors as readonly string[]).includes(value);
}
Enum Alternative
const Direction = {
  Up: "UP",
  Down: "DOWN",
  Left: "LEFT",
  Right: "RIGHT",
} as const;

type Direction = (typeof Direction)[keyof typeof Direction];
// "UP" | "DOWN" | "LEFT" | "RIGHT"

function move(dir: Direction) {
  console.log("Moving", dir);
}

move(Direction.Up); // OK
move("UP");         // OK
// move("DIAGONAL"); // Error
Config Object
const API_CONFIG = {
  baseUrl: "https://api.example.com",
  version: "v2",
  endpoints: {
    users: "/users",
    posts: "/posts",
    auth: "/auth/login",
  },
  retries: 3,
} as const;

type Endpoint = (typeof API_CONFIG.endpoints)[keyof typeof API_CONFIG.endpoints];
// "/users" | "/posts" | "/auth/login"

Common Use Cases

  • 1Creating union types from arrays of string literals
  • 2Enum-like constant objects with precise types
  • 3Deeply readonly configuration objects
  • 4Preserving tuple types for function arguments
  • 5Single-source-of-truth constant definitions

Understanding as const assertion

The as const assertion tells TypeScript to infer the narrowest possible type for a value. Arrays become readonly tuples with literal element types, objects become deeply readonly with literal property types, and primitives become their literal type rather than the general base type.

Without as const, const colors = ["red", "green", "blue"] is typed as string[]. With as const, it becomes readonly ["red", "green", "blue"]—a tuple of exactly three elements with specific literal types. This precision is what enables deriving union types: (typeof colors)[number] produces "red" | "green" | "blue".

The "enum-like object" pattern is one of the most popular uses of as const. By defining const Direction = { Up: "UP", Down: "DOWN" } as const and extracting the value union, you get an enum-like structure that is fully tree-shakeable, works with plain JavaScript, and produces real values (unlike TypeScript enums which can have surprising behavior).

as const applies deeply. Nested objects and arrays all become readonly with literal types. This makes it perfect for deeply structured configuration objects where you want TypeScript to know the exact shape and values at compile time.

Combined with satisfies (TypeScript 4.9+), as const becomes even more powerful. While as const preserves literal types, it doesn't validate shape. Adding satisfies validates the value against a type while as const preserves the precision. The pattern value satisfies Type is often preferred over as const in modern code because it provides validation.

One important behavior: as const makes everything readonly, which means the resulting type is not assignable to mutable versions. If a function expects string[], you can't pass a readonly ["red"] directly. You may need a type assertion or to adjust the function signature to accept readonly arrays.

Related Types

More Advanced Patterns

Explore TypeScript Types

Browse our complete reference of 30 TypeScript utility types with definitions, examples, and explanations.