Overview
TypeScript’s ‘keyof’ type operator provides a way to use the keys of an object as a type. It allows developers to create more robust and flexible code by ensuring that the string they use is actually a key on a specific object.
What is ‘keyof’ and How to Use It?
In TypeScript, keyof
is a type operator that takes an object type and produces a string or numeric literal union of its keys. This is particularly handy when we want to make sure the string we are working with is a guaranteed property of an object.
interface Person {
name: string;
age: number;
}
let personProp: keyof Person; // 'name' | 'age'
personProp = 'name'; // OK
personProp = 'age'; // OK
personProp = 'height'; // Error: Type '"height"' is not assignable to type '"name" | "age"'.
Using ‘keyof’ with Generics
The true power of keyof
is revealed when used with generics. Here’s how you can enforce function parameters to be a property of a given object:
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const person = {
name: 'Alice',
age: 25
};
const personName = getProperty(person, 'name'); // OK
const personAge = getProperty(person, 'age'); // OK
const personHeight = getProperty(person, 'height'); // Error: Argument of type '"height"' is not assignable to parameter of type '"name" | "age"'.
Dynamically Creating Object Types with ‘keyof’
Another advanced usage of keyof
is dynamically creating object types based on existing object keys.
interface Person {
name: string;
age: number;
location: string;
}
type ReadonlyPerson = {
[K in keyof Person]: Readonly<Person[K]>;
};
const person: ReadonlyPerson = {
name: 'Bob',
age: 30,
location: 'New York'
};
// All properties of person are now readonly, thus the following will raise an error
person.age = 31; // Error: Cannot assign to 'age' because it is a read-only property.
Combining ‘keyof’ with Mapped Types and Conditional Types
keyof
becomes even more powerful when combined with mapped and conditional types. Suppose you want to have a function that returns object values but only for keys that correspond to string properties.
interface Person {
name: string;
age: number;
location: string;
}
function getStringProperties(obj: T) {
const result: {[K in keyof T]: T[K] extends string ? T[K] : never} = {};
for (const key in obj) {
if (typeof obj[key] === 'string') {
result[key] = obj[key];
}
}
return result;
}
const person = {
name: 'Eve',
age: 28,
location: 'London'
};
const stringPropsOfPerson = getStringProperties(person);
// { name: 'Eve'; location: 'London'; age: never }
Summary
The keyof
type operator is an essential feature in TypeScript’s type system. It enables developers to create self-contained, type-safe code that can adapt dynamically to changes in data structures. With the proper understanding and judicious use, leveraging keyof
will certainly level up your TypeScript programming skills.