Suggest an editImprove this articleRefine the answer for “How to constrain a generic type to the keys of another type?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)The simplest way is **`K extends keyof T`**: `keyof T` gives the union of `T`'s keys, and the parameter `K` is constrained to that union, for example `function getProp<T, K extends keyof T>(obj: T, key: K): T[K]`. **Key point:** this base pattern can be narrowed further - by intersecting with a specific set of keys, filtering by value type, or via `Extract<keyof T, number>` for arrays.Shown above the full answer for quick recall.Answer (EN)Image## 1) Base pattern: an object's key ```javascript function getProp<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user = { id: 1, name: "Tim" }; getProp(user, "id"); // OK getProp(user, "age"); // Error: "age" ∉ keyof typeof user ``` - `keyof T` is the union of the string/number/symbol keys of type `T`. --- ## 2) Allowing only a subset of keys Sometimes you need not **all** of `T`'s keys, but only part of them: ```javascript function getIdOrName< T, K extends keyof T & ("id" | "name") >(obj: T, key: K): T[K] { return obj[key]; } ``` - Here `K` is the intersection of "keys of T" with a specific set `"id" | "name"`. --- ## 3) Keys whose **values** match a type (filtering by value) ```javascript type KeysOfType<T, V> = { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T]; function getNumberField< T, K extends KeysOfType<T, number> >(obj: T, key: K): number { return obj[key] as number; } type U = { a: number; b: string; c: number }; /* K = "a" | "c" */ ``` This kind of technique is often called *value-based key filtering*. --- ## 4) Required/optional/non-writable keys Sometimes you need to restrict keys by a "qualifier" (optional/readonly): ```javascript type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T]; type WritableKeys<T> = { [K in keyof T]-?: { [P in K]: T[P] } extends { -readonly [P in K]: T[P] } ? K : never }[keyof T]; function takeRequired<T, K extends RequiredKeys<T>>(obj: T, key: K) { return obj[key]; } ``` --- ## 5) Constraining by an **index signature**'s keys If a type has a dictionary shape: ```javascript type Dict = Record<string, number>; // index signature function getDict< T extends Record<string, unknown>, K extends keyof T >(obj: T, key: K) { return obj[key]; } ``` > `keyof T` for an index signature is roughly `string | number | symbol` (depends on the keys). --- ## 6) Keys of arrays and tuples ```javascript function first<T extends readonly unknown[]>(arr: T) { const i: Extract<keyof T, number> = 0; // numeric keys only return arr[i]; // T[number] } ``` For an array, `keyof T` also includes methods (`"length"`, `"push"`). To restrict to indexes, intersect with `number`. --- ## 7) Keys of literal objects: `as const` To get **literal** keys instead of "widened" ones: ```javascript const cfg = { api: "/api", cdn: "/cdn", } as const; type Cfg = typeof cfg; // { readonly api: "/api"; readonly cdn: "/cdn" } type CfgKey = keyof Cfg; // "api" | "cdn" function fromCfg<K extends keyof Cfg>(k: K): Cfg[K] { return cfg[k]; } ``` --- ## 8) Keys of an `enum` / lookup objects ```javascript enum Role { Admin = "admin", User = "user" } const ROLES = { Admin: "admin", User: "user" } as const; type RoleKey = keyof typeof ROLES; // "Admin" | "User" type RoleVal = typeof ROLES[RoleKey]; // "admin" | "user" function allow<K extends keyof typeof ROLES>(k: K) { /* ... */ } ``` --- ## 9) Utilities for building an API: `Pick`/`Record`/`Omit` with constrained keys ```javascript function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> { const out = {} as Pick<T, K>; keys.forEach(k => (out[k] = obj[k])); return out; } function mapValues<T, K extends keyof T>( obj: T, keys: K[], fn: (v: T[K], k: K) => T[K] ): Pick<T, K> { const res = {} as Pick<T, K>; for (const k of keys) res[k] = fn(obj[k], k); return res; } ``` --- ## 10) Common pitfalls and tips - `keyof never` -> `never`: if `T` can be `never`, `K` is already uninferable; add a guard. - For arrays, don't forget to filter keys down to `number`, otherwise the string methods show up too. - If the keys come from a value (an array of strings), prefer `as const`: ```javascript const allowed = ["id", "name"] as const; type Allowed = typeof allowed[number]; // "id" | "name" function f<T, K extends keyof T & Allowed>(obj: T, key: K) {} ``` --- ### Summary - Base: `K extends keyof T`, the keys of `T`. - Subset: `K extends keyof T & ("a" | "b")`. - By value type: `KeysOfType<T, V>`. - For arrays: `Extract<keyof T, number>`. - For literals/enums: `keyof typeof X` (+ `as const`).For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.