Skip to content
TypeScript ts advanced-types 4 min read

The typeof Operator

TypeScript’s typeof operator has two distinct lives. In an expression position it is the familiar JavaScript runtime operator that returns a string like "string" or "object". In a type position it is a type query that captures the static type of a value and lets you reuse it. This page is about the type-level typeof: deriving a type from a constant, a function, or a configuration object so your types stay in lockstep with your real data. It pairs constantly with keyof and as const.

Type query basics

Place typeof in a type context — after a colon, inside a type alias, or as a generic argument — and it yields the inferred type of the referenced value. This avoids manually re-describing a shape you already wrote as a value.

const point = { x: 10, y: 20 };

type Point = typeof point; // { x: number; y: number }

const another: Point = { x: 0, y: 0 }; // ✅

Point is derived from point, not declared separately. If you change the value, the type follows. Note the values are widened to number here; use as const (below) when you want the literal 10 and 20 preserved.

typeof vs the runtime operator

The same keyword behaves differently depending on where it appears. In value space it produces a string at runtime; in type space it produces a type at compile time and emits nothing.

const value = 42;

// Runtime operator (value position) — evaluates at runtime:
if (typeof value === "number") {
  console.log("it's a number");
}

// Type query (type position) — erased after compilation:
type V = typeof value; // number
ContextMeaningResultRuntime cost
typeof x === "..."Runtime checkA stringYes
let y: typeof xType queryA typeNone
keyof typeof xKeys of a value’s typeKey unionNone

Deriving types from constants with as const

Combine typeof with as const to turn a plain object or array into a precise, deeply-readonly type. Without as const, properties widen to general types; with it, you keep exact literals.

const ROLES = ["admin", "editor", "viewer"] as const;

type Role = (typeof ROLES)[number]; // "admin" | "editor" | "viewer"

const HTTP = {
  ok: 200,
  notFound: 404,
} as const;

type StatusName = keyof typeof HTTP; // "ok" | "notFound"
type StatusCode = (typeof HTTP)[keyof typeof HTTP]; // 200 | 404

(typeof ROLES)[number] reads the element type of the readonly tuple — a clean way to derive a union from an array of values. The HTTP example shows the trio of keyof, typeof, and indexed access working together.

Use this single-source-of-truth pattern instead of declaring an enum plus a matching array. The value drives the type, so they can never disagree.

typeof on functions and classes

typeof also captures the type of functions and class constructors, which is handy for higher-order utilities and for referring to a constructor itself.

function createUser(name: string, age: number) {
  return { name, age, id: crypto.randomUUID() };
}

type CreateUser = typeof createUser; // (name: string, age: number) => { ... }
type User = ReturnType<typeof createUser>; // { name: string; age: number; id: string }

class Logger {}
type LoggerClass = typeof Logger; // the constructor type, not an instance
const ctor: LoggerClass = Logger;

typeof createUser gives the full function signature, which you can feed into ReturnType or Parameters. For a class, typeof Logger is the constructor, distinct from Logger used as an instance type.

A practical config example

A common real-world use is deriving types from a configuration object so consumers get exact keys and value types for free.

const theme = {
  colors: { primary: "#0070f3", danger: "#e00" },
  spacing: [0, 4, 8, 16],
} as const;

type Theme = typeof theme;
type ColorName = keyof Theme["colors"]; // "primary" | "danger"

function getColor(name: ColorName): string {
  return theme.colors[name];
}

Theme mirrors the literal config, and ColorName is restricted to keys that actually exist. Renaming a color in the value immediately surfaces every stale usage as a type error.

Best Practices

  • Use type-level typeof to derive types from existing values rather than re-declaring shapes.
  • Add as const when you need literal values and readonly arrays/tuples preserved.
  • Derive value unions from arrays with (typeof arr)[number].
  • Combine keyof typeof obj to get a value’s key union in one expression.
  • Reach for ReturnType<typeof fn> and Parameters<typeof fn> to reuse function shapes.
  • Keep configuration as a single source of truth and let typeof generate the types.
Last updated June 29, 2026
Was this helpful?