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: stringage: 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'
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
getUserProperty({name: 'Amir', age: 36}, 'age');Result:
36
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"'.
That function is easy to write because we can see the
Usertype. It has the properties "name" and "age", so the function'skeyparameter 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 keysTwill have in advance. WhenTisUser, the key type is'name' | 'age'. But whenTisAlbum, the key type might need to be'albumName' | 'copiesSold'.The solution here is TypeScript's
keyoftype 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: stringage: number};- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
const key: keyof User = 'name';key;Result:
'name'
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
const key: keyof User = 'age';key;Result:
'age'
- 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'.
Here's a code problem:
The
getPropertyfunction below should return the requested object property. For example,getProperty({name: 'Amir'}, 'name')should give us'Amir'.getPropertyis generic, so it works for any object type.getPropertytakes two parameters: the object and the property name. Currently,propertyNamehas the typestring, but that's wrong and causes a type error. (TypeScript won't let us accessobj[propertyName]ifpropertyNameis a string. That would allow us to access properties that don't exist!)Modify
propertyName's type to usekeyof, statically guaranteeing that we can only pass apropertyNamethat's actually a property of the typeT. 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]