Execute Program

Advanced TypeScript: Keyof

Welcome to the Keyof lesson!

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • Suppose that we have an object with the type {name: string, age: number}. We want to write a function that gets properties from those users:

  • >
    type User = {
    name: string
    age: number
    };

    function getUserProperty(user: User, key: 'name' | 'age') {
    return user[key];
    }
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    getUserProperty({name: 'Amir', age: 36}, 'name');
    Result:
    'Amir'Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    getUserProperty({name: 'Amir', age: 36}, 'age');
    Result:
    36Pass Icon
  • If we ask for a property name that isn't in the type, that's a type error. It violates our function's 'name' | 'age' parameter type.

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    getUserProperty({name: 'Amir', age: 36}, 'isAdmin');
    Result:
    type error: Argument of type '"isAdmin"' is not assignable to parameter of type '"name" | "age"'.Pass Icon
  • That function is easy to write because we can see the User type. It has the properties "name" and "age", so the function's key parameter is a 'name' | 'age'.

  • Now imagine that we want a generic version of our function: getProperty<T>(obj: T, key: ???). There's no way to know what property keys T will have in advance. When T is User, the key type is 'name' | 'age'. But when T is Album, the key type might need to be 'albumName' | 'copiesSold'.

  • The solution here is TypeScript's keyof type operator. It gives us an object type's property names as a union of literal strings. For example, keyof {name: string, age: number} is the type 'name' | 'age'.

  • >
    type User = {
    name: string
    age: number
    };
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    const key: keyof User = 'name';
    key;
    Result:
    'name'Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    const key: keyof User = 'age';
    key;
    Result:
    'age'Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    const key: keyof User = 'isAdmin';
    key;
    Result:
    type error: Type '"isAdmin"' is not assignable to type 'keyof User'.Pass Icon
  • Here's a code problem:

    The getProperty function below should return the requested object property. For example, getProperty({name: 'Amir'}, 'name') should give us 'Amir'. getProperty is generic, so it works for any object type.

    getProperty takes two parameters: the object and the property name. Currently, propertyName has the type string, but that's wrong and causes a type error. (TypeScript won't let us access obj[propertyName] if propertyName is a string. That would allow us to access properties that don't exist!)

    Modify propertyName's type to use keyof, statically guaranteeing that we can only pass a propertyName that's actually a property of the type T. You won't need to change any other part of the function.

    function getProperty<T>(obj: T, propertyName: keyof T) {
    return obj[propertyName];
    }
    const name: string = getProperty({name: 'Amir'}, 'name');
    const age: number = getProperty({age: 36}, 'age');
    [name, age];
    Goal:
    ['Amir', 36]
    Yours:
    ['Amir', 36]Pass Icon