Using ‘Keyof’ Type Operator in TypeScript

Updated: January 7, 2024 By: Guest Contributor Post a comment

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.