History of the issue
TypeScript has been developed from the very beginning for strict typing of objects and writing more predictable code in JavaScript. One of the tools for ensuring safety when accessing object properties became the keyof operator, which appeared in TypeScript 2.1. It allows creating types that represent a set of string (and symbol) keys available in the declared object type.
The problem
In pure JavaScript, object properties can be accessed by any string, and if there is a mistake - it will result in undefined or an error that will be found only at runtime. Without strict typing, it's easy to make a typo or forget to update a key string during refactoring. Additionally, there are often needs to build a function that only accepts valid keys of a specific object or type.
The solution
The keyof operator creates a union type from all the keys of a type. With its help, one can limit valid keys, increase API safety (e.g., for getter/setter functions), and create generic utility types.
Example code:
type User = { name: string; age: number; active: boolean }; type UserKey = keyof User; // "name" | "age" | "active" function getProp<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user: User = { name: 'Tom', age: 33, active: true }; const age = getProp(user, 'age'); // age type is number
Key features:
What does keyof return for an array, and can you get indexes?
keyof for an array returns the types of keys of an object, which the array actually is in JS. Typically, this includes index strings and special array properties.
Example code:
type A = keyof number[]; // "length" | "toString" | "pop" | ... | number
Can keyof return keys of union-types?
Yes, keyof returns the intersection of the keys of both objects from the union type, not their union.
Example code:
type A = {a: string, b: number } type B = {b: number, c: boolean } type C = keyof (A | B); // "b"
What happens if an object's property is optional? Does keyof support "?"?
Yes, optional properties are also included in the resulting type of keys, but that does not mean the property is necessarily present on the object at runtime.
Example code:
type D = { a: string; b?: number }; type DK = keyof D; // "a" | "b"
A function getProperty(obj, key) is written with a string type key and called with a non-existent key - an error appears only at runtime.
Pros:
Cons:
Generics are used: getProperty<T, K extends keyof T>(), the key type is strictly constrained to the keys of the structure.
Pros:
Cons: