liveBook · Manning

Keyof operator

As explained in Essential TypeScript 5, Third Edition

The keyof operator is a TypeScript type operator that produces a union of the property names of an object type.

Use `keyof` when a property name must belong to a particular TypeScript type. It produces a union of that type’s property names. For a `Product` with `name: string` and `price: number`, `keyof Product` is `"name" | "price"`. A variable of that type accepts either name, but assigning `"someOtherName"` produces a compiler error. section 13.4.1

Constrain a property argument

In `src/index.ts`, a generic function can accept only keys present on its argument: function getValue<T, K extends keyof T>(item: T, keyname: K) { console.log(`Value: ${item[keyname]}`); } For a `Product`, `keyname` can be `"name"` or `"price"`; for an `Employee`, it can be `"name"` or `"role"`. Type arguments are normally inferred. If written explicitly as `getValue<Product, "name">(p, "name")`, the first `"name"` is a type argument used for checking; the second is the value passed at runtime. section 13.4.1 section 13.4.2

`keyof` supplies names, not property value types. Combine it with indexed access: `Product["price"]` is `number`, while `Product[keyof Product]` is `string | number`. In a generic getter, `K extends keyof T` checks the key and return type `T[K]` preserves the selected property’s type. section 13.4.3

Listing 13.12 Using the indexed access operator in the index.ts file in the src folder
import { City, Person, Product, Employee } from "./dataTypes.js";

function getValue<T, K extends keyof T>(item: T, keyname: K): T[K] {
    return item[keyname];
}

let p = new Product("Running Shoes", 100);
console.log(getValue<Product, "name">(p, "name"));
console.log(getValue(p, "price"));

let e = new Employee("Bob Smith", "Sales");
console.log(getValue(e, "name"));
console.log(getValue(e, "role"));

The getter listing shows `keyof` restricting the argument and `T[K]` typing the returned value. section 13.4.3

The same pairing lets a collection choose its storage key instead of requiring every object to have a `name` property. With `Collection<T, K extends keyof T>`, the chosen property name has type `K`, and the map key and `get` argument use `T[K]`. Choosing a product’s `price` makes `get(100)` a numeric lookup; the compiler can infer the generic arguments from the constructor call. section 13.4.4

Mapped types use `keyof` to enumerate properties. `{ [P in keyof Product]: Product[P] }` recreates the product’s property names and types. To keep selected properties, constrain `K` with `keyof T` and map `[P in K]: T[P]`; the built-in `Pick<T, K>` serves the same selection role, while `Omit<T, K>` excludes named properties. section 13.5 section 13.5.4

Check which type receives `keyof`. If a generic argument is `Product[]`, `keyof` yields array property names—not `Product` property names—so `"price"` fails as its key. A conditional type using `infer` can extract the array element type before applying `keyof`, giving both `Product` and `Product[]` the `"name" | "price"` key union. section 13.6.6

How Essential TypeScript 5, Third Edition covers it

How deep it goes
Works through compiler errors, generic getters, indexed access, collections, and mapped types, including an array-type pitfall.
Examples
Uses Product and Employee property lookups, a keyed Collection backed by Map, and Pick and Omit mappings.
What it assumes
Assumes familiarity with object properties and generic types; defines the `keyof` union before combining it with indexed access.
What you can do after
Build it from scratch: constrain a generic getter or collection key and preserve its property value type with `T[K]`.
How it connects
Connects literal value types, generic constraints, indexed access types, mapped types, and conditional types.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Essential TypeScript 5, Third Edition. The section links point to the parts of the book it draws on.