TS keyof
keyof T gives you the union of T's property keys as literal types. Pairs perfectly with generic constraints to build type-safe property accessors.
Basics
TS
type Point = { x: number; y: number };
type Axis = keyof Point; // 'x' | 'y'
const a: Axis = 'x';
// const b: Axis = 'z'; // ✗ error
Type-safe property access
TS
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { id: 1, name: 'Ada' };
const id = getProp(user, 'id'); // number
const name = getProp(user, 'name'); // string
// getProp(user, 'email'); // ✗ error — not a key
Index signatures
TS
type StringMap = { [key: string]: number };
type K = keyof StringMap; // string | number (indexable by either at runtime)
keyof on a Record
TS
type Colors = Record<'red' | 'green' | 'blue', string>; type Color = keyof Colors; // 'red' | 'green' | 'blue'
keyof + typeof — strongly typed enums from values
TS
const STATUSES = {
Paid: 'paid',
Pending: 'pending',
Refunded: 'refunded',
} as const;
type StatusKey = keyof typeof STATUSES; // 'Paid' | 'Pending' | 'Refunded'
type StatusValue = typeof STATUSES[StatusKey]; // 'paid' | 'pending' | 'refunded'
Common mistake — keyof object
TS
// "object" has no known keys type K = keyof object; // never // You almost always want keyof of a SPECIFIC shape.
Tip:
keyof + generic constraint is the building block of every "typed pluck/get/set" helper you'll ever need. Memorise function get<T, K extends keyof T>(o: T, k: K): T[K] — it pays dividends forever.Example
Example
type Point = { x: number; y: number };
type Axis = keyof Point; // 'x' | 'y'
function get<K extends keyof Point>(p: Point, k: K): Point[K] {
return p[k];
}
console.log(get({ x: 3, y: 4 }, 'x'));
Try it Yourself »
Exercise
Operator that gives the union of T's keys.
type K =
Point;
Five letters.
Discussion
Loading…