satisfies + as const cheatsheet.

The widening problem

const config = {
  host: "localhost",
  port: 5432,
};
// type: { host: string; port: number }
// — lost the literals

To keep "localhost" and 5432:

const config = {
  host: "localhost",
  port: 5432,
} as const;
// type: { readonly host: "localhost"; readonly port: 5432 }

satisfies (TS 4.9+)

type Config = Record<string, string | number>;

const config = {
  host: "localhost",
  port: 5432,
} satisfies Config;

// Validates against Config, but type stays literal:
// { host: "localhost"; port: 5432 }

vs annotation:

const config: Config = { host: "localhost", port: 5432 };
// type: Record<string, string | number> — too wide

Why prefer satisfies over annotation

type Routes = Record<string, { method: "GET" | "POST" }>;

const routes = {
  users: { method: "GET" },
  posts: { method: "POST" },
} satisfies Routes;

routes.users.method;     // "GET" (literal)
routes.users;            // exists (not optional)

With annotation : Routes, routes.users would be { method: "GET" | "POST" } | undefined.

satisfies + as const

const config = {
  host: "localhost",
  port: 5432,
} as const satisfies Config;
// Combine readonly literals + validation

Deriving types

const config = {
  host: "localhost",
  port: 5432,
} as const;

type Host = typeof config.host;      // "localhost"
type Keys = keyof typeof config;     // "host" | "port"

Enum replacement

const Status = {
  Active: "active",
  Inactive: "inactive",
} as const;

type Status = typeof Status[keyof typeof Status];
// "active" | "inactive"

function f(s: Status) { ... }
f(Status.Active);
f("active");

Lighter than enum, no runtime code generated beyond the object.

Arrays as const

const days = ["mon", "tue", "wed"] as const;
type Day = typeof days[number];      // "mon" | "tue" | "wed"

for (const d of days) {
  // d: "mon" | "tue" | "wed"
}

satisfies for function defaults

type Theme = { primary: string; bg: string };

const defaultTheme = {
  primary: "#000",
  bg: "#fff",
} satisfies Theme;

// Use defaultTheme.primary as known "#000"
// Pass it where a Theme is required

satisfies for handlers

type Handler = (req: Request) => Promise<Response>;

const handlers = {
  users: async (req) => new Response("..."),
  posts: async (req) => new Response("..."),
} satisfies Record<string, Handler>;

handlers.users;          // (req: Request) => Promise<Response>

Inference from objects

function defineRoutes<T extends Record<string, unknown>>(routes: T): T {
  return routes;
}

const r = defineRoutes({
  users: { method: "GET" as const },
  posts: { method: "POST" as const },
});

r.users.method;     // "GET"

Generic identity function preserves literal inference.

NoInfer for hint-only types

function call<T>(value: T, onError: (e: NoInfer<T>) => void) { ... }

call("hi", (x) => x.toUpperCase());
// T inferred from "hi", not from the callback

Const type parameters (5.0+)

function arr<const T extends readonly any[]>(xs: T): T {
  return xs;
}

const a = arr(["a", "b"]);     // readonly ["a", "b"]

Like as const but on the function param.

When NOT to satisfy

function takeConfig(c: Config) { ... }

const cfg = {} satisfies Config;

If you’re going to pass it to a function that takes the wider type, the literal benefits don’t matter. Just annotate.

Typing JSON-like literals

const cfg = {
  routes: [
    { path: "/", method: "GET" },
    { path: "/x", method: "POST" },
  ],
} satisfies {
  routes: Array<{ path: string; method: "GET" | "POST" | "PUT" }>;
};

Catches typos in method while preserving the structure.

Common mistakes

  • : Type instead of satisfies Type when you want literal types preserved.
  • as Type instead of satisfies Typeas is a cast, no check.
  • Forgetting as const — array becomes string[] instead of tuple.
  • as const on values passed to mutable APIs — caller may push to your readonly array.

Read this next

If you want my config + routes helpers, they’re at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .