Topics
v5.0.0
On this page · sections

Advanced Type Operators

TypeScript provides type operators that compute types from other types — typeof, keyof, indexed access, template literals, and infer. Senior frontend engineers use them for typed routes, event systems, design tokens, and keeping client code in sync with runtime values.

OperatorInputOutput
typeofValueType of value
keyofObject typeUnion of keys
T[K]Object + keyProperty type
`${A}-${B}`String unionsCombined string union
inferPattern in conditionalExtracted inner type

typeof — types from values

Definition

typeof at the type level extracts the type of a value (variable, function, const object):

const config = { apiUrl: "/api", retries: 3, features: { darkMode: true }, } as const; type Config = typeof config; // { readonly apiUrl: "/api"; readonly retries: 3; ... } type ApiUrl = typeof config.apiUrl; // "/api"

Real-world example — infer from factory

function createStore<T extends Record<string, unknown>>(initial: T) { let state = initial; return { getState: () => state, setState: (patch: Partial<T>) => { state = { ...state, ...patch }; }, }; } const store = createStore({ count: 0, user: null as string | null }); type StoreState = ReturnType<typeof store.getState>; // { count: number; user: string | null }

typeof vs runtime typeof

// Runtime — JavaScript operator typeof "hello"; // "string" // Type level — TypeScript operator type S = typeof "hello"; // "hello" (literal type when const)

Interview: Pair typeof with as const to get narrow literal unions from runtime constants.

keyof — advanced usage

Definition

Beyond simple objects, keyof works on arrays, tuples, and mapped results:

type Colors = { red: string; blue: string }; type ColorKey = keyof Colors; // "red" | "blue" type NumTuple = [number, string, boolean]; type TupleKey = keyof NumTuple; // "0" | "1" | "2" | "length" | ...

keyof with generics

function pluck<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const name = pluck(user, "name"); // string

Filter keys with conditional mapped types

type KeysOfType<T, V> = { [K in keyof T]: T[K] extends V ? K : never; }[keyof T]; type User = { id: string; name: string; age: number }; type StringKeys = KeysOfType<User, string>; // "id" | "name"

Indexed access types

Definition

Look up property types using literal or union keys:

type User = { id: string; profile: { bio: string; avatar: string }; }; type Bio = User["profile"]["bio"]; // string type ProfileSlice = User["id" | "profile"]; // string | { bio: string; avatar: string }

Array element access

type Item = { id: string }[]; type Element = Item[number]; // { id: string } type Head<T extends readonly unknown[]> = T extends readonly [infer H, ...unknown[]] ? H : never; type First = Head<[string, number]>; // string

Lookup from const arrays

const STATUSES = ["idle", "loading", "error"] as const; type Status = (typeof STATUSES)[number]; // "idle" | "loading" | "error"

Template literal types

Definition

Build string types from patterns, similar to template strings at runtime:

type Greeting = `Hello, ${string}`; const a: Greeting = "Hello, world"; // ✅ const b: Greeting = "Hi, world"; // ❌

Combine literal unions — TypeScript takes the Cartesian product:

type Color = "red" | "blue"; type Size = "sm" | "lg"; type Variant = `${Color}-${Size}`; // "red-sm" | "red-lg" | "blue-sm" | "blue-lg"

Interview: Template literals distribute over unions in each ${} slot.

Typed event names

Real-world example

type Entity = "user" | "order"; type Action = "created" | "updated" | "deleted"; type DomainEvent = `${Entity}:${Action}`; // "user:created" | ... | "order:deleted" function emit(event: DomainEvent, payload: unknown) { analytics.track(event, payload); } emit("user:created", { id: "1" }); // ✅ emit("user:invalid", {}); // ❌

DOM-style custom events

type WidgetEvent = `widget:${"open" | "close" | "submit"}`; function onWidgetEvent(name: WidgetEvent, handler: () => void) { document.addEventListener(name, handler); }

CSS and design tokens

type CSSProperty = "margin" | "padding"; type Side = "Top" | "Right" | "Bottom" | "Left"; type CSSPropertyWithSide = `${CSSProperty}${Side}`; // "marginTop" | "marginRight" | ... | "paddingLeft" type StyleKey = `--${string}`; const token: StyleKey = "--color-primary"; // ✅

Token scales

type TokenCategory = "color" | "spacing" | "font"; type TokenScale = "sm" | "md" | "lg"; type DesignToken = `${TokenCategory}-${TokenScale}`; const spacing: Record<DesignToken, string> = { "color-sm": "#000", "color-md": "#333", "color-lg": "#666", "spacing-sm": "4px", "spacing-md": "8px", "spacing-lg": "16px", "font-sm": "12px", "font-md": "14px", "font-lg": "18px", };

Built-in string transforms

type Upper = Uppercase<"hello">; // "HELLO" type Lower = Lowercase<"HELLO">; // "hello" type Cap = Capitalize<"hello">; // "Hello" type Uncap = Uncapitalize<"Hello">; // "hello"

camelCase from snake_case

type CamelCase<S extends string> = S extends `${infer Head}_${infer Tail}` ? `${Head}${Capitalize<CamelCase<Tail>>}` : S; type Camel = CamelCase<"user_id">; // "userId"

Typed routes

type StaticRoute = "/" | "/about" | "/settings"; type DynamicSegment = `[${string}]`; type AppRoute = StaticRoute | `/users/${DynamicSegment}` | `/posts/${DynamicSegment}`; function navigate(path: AppRoute) { router.push(path); } navigate("/users/[id]"); // ✅

Production apps often generate route types from a file-based router or a central route map.

as const + template literals

const EVENTS = ["click", "focus", "blur"] as const; type EventName = (typeof EVENTS)[number]; type HandlerName = `on${Capitalize<EventName>}`; // "onClick" | "onFocus" | "onBlur"

Bridges runtime constants and compile-time string unions — the pattern behind many React prop typings.

Limitations

Very large union products → slow type checking (avoid huge cartesian expansions) Runtime strings → won't match unless validated and narrowed Template types erase at runtime → still need runtime checks for user input Recursive string parsing → depth limits; prefer code gen for complex paths

Senior Interview Summary

What to say

typeof bridges runtime values and static types. keyof and indexed access derive shapes from existing types. Template literal types compose string unions for events, tokens, and routes. infer extracts pieces from matched patterns.

Operator cheat sheet

typeof value → type of a value keyof T → union of keys T[K] → property type at K `${A}-${B}` → string union product infer X → capture in conditional match

Common patterns

typeof + as const → literal unions from constants keyof typeof obj → keys of runtime object T[number] → array element type `${Entity}:${Action}` → domain events Capitalize + as const → React handler prop names

Practical rule

Finite known sets → template literal unions User-provided strings → validate at runtime, then narrow Huge cross products → split types or use branded strings Derive from source → typeof / keyof before hand-copying

Last updated on