CodeOath
← All posts
TypeScript75 min total · 21 parts

TypeScript Fundamentals: Types, Interfaces, Generics, and Why It Catches Bugs Before Runtime

Contents — Part 12 of 21: Generic Constraints and keyof/typeof
Part 12 of 21 · ~1 min

Generic Constraints and keyof/typeof

An unconstrained generic (<T>) can be anything, which means the function body can't assume it has any particular property. extends constrains what T is allowed to be:

function getLength<T extends { length: number }>(item: T): number {
  return item.length; // safe — every T is guaranteed to have .length
}

getLength("hello");     // fine — strings have .length
getLength([1, 2, 3]);   // fine — arrays have .length
getLength(42);          // Error: number doesn't satisfy the constraint

keyof produces a union of an object type's property names as string literal types — the standard tool for writing a generic, type-safe "get a property by name" function:

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { id: 1, name: "Ada" };
getProperty(user, "name"); // returns string, and only "id" | "name" are valid keys
getProperty(user, "email"); // Error: Argument of type '"email"' is not assignable

K extends keyof T is what makes key genuinely constrained to user's actual keys instead of any arbitrary string, and T[K] (an indexed access type) is how the return type tracks exactly which property was requested rather than collapsing to a union of all possible property types.

typeof, used in a type position (not the runtime typeof from the narrowing section), extracts the type of an existing value — handy for deriving a type from a constant instead of maintaining two definitions in sync:

const defaultConfig = { retries: 3, timeout: 5000 };
type Config = typeof defaultConfig; // { retries: number; timeout: number }