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 }