Everyday TypeScript: Indexing Into Object Types
Welcome to the Indexing Into Object Types lesson!
This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!
When working with objects, we can access their individual properties. (They wouldn't be much use otherwise!)
>
const album = {name: 'Kind of Blue',lengthMinutes: 45,};const albumName = album['name'];albumName;Result:
'Kind of Blue'
We can do the same with object types themselves. If
Albumis the type{name: string, copiesSold: number}, thenAlbum['name']is the typestring.>
type Album = {name: stringlengthMinutes: number};type AlbumName = Album['name'];const albumName: AlbumName = 'Kind of Blue';albumName;Result:
'Kind of Blue'
If we control the type definition, extracting property types this way is rarely necessary. We can just define a new shared type. Then we can reference it in multiple places.
Here's an example of that with our album example. We define the
AlbumNametype directly, rather than using the type indexing feature.>
type AlbumName = string;type Album = {name: AlbumNamecopiesSold: number};const albumName: AlbumName = 'Kind of Blue';albumName;Result:
'Kind of Blue'
Let's switch to a more realistic example. Suppose that we're using a third-party library to connect to a database. That library provides us with TypeScript types, including a
ConnectionOptionstype:>
type ConnectionOptions = {host: string // the IP address or hostname of the serverport: numbercredentials: {username: stringpassword: string}};Now we want to write a
buildConnectionOptionsfunction as part of our database setup code. It should take ahost, aport, andcredentials. But the authors of our database library didn't give thecredentialstype a name! As we can see in the definition above, that object type is defined inline inside of theConnectionOptionstype.We could choose to duplicate the
credentialstype definition in our own code. In some cases that's fine. But when the types are complex or deeply nested, duplicating them is usually a bad idea. Instead, we can reference thecredentialstype directly.Here's a code problem:
The function below wants to take some credentials as an argument. Fill in the missing type by referencing the credentials type inside of the
ConnectionOptionstype. You won't need to change any other part of the code.type ConnectionOptions = {host: stringport: numbercredentials: {username: stringpassword: string}};function buildConnectionOptions(host: string,port: number,credentials: ConnectionOptions['credentials']): ConnectionOptions {return {host, port, credentials};}buildConnectionOptions('localhost',5432,{username: 'mulder', password: 'TrustNo1'});- Goal:
{host: 'localhost', port: 5432, credentials: {username: 'mulder', password: 'TrustNo1'}}- Yours:
{host: 'localhost', port: 5432, credentials: {username: 'mulder', password: 'TrustNo1'}}
There's one limitation that might be surprising. Above, we accessed property types like
Album['name']. You might assume that we can also write that asAlbum.name, but we can't! This feature only works with square brackets, not with the dot operator.>
type Album = {name: stringcopiesSold: number};type AlbumName = Album.name;Result:
This type indexing feature hints at something that we'll see repeatedly in more advanced lessons: TypeScript's type definitions are their own complex language.
New TypeScript programmers often think of TypeScript as a way to ensure that their number variables are actually numbers and their string variables are actually strings. It's true that it can ensure those things. But we're not limited to simple types like
numberandstring. We can build types out of types!JavaScript has many operators to transform values into other values. We can build objects with
{...}; we can access object properties withobj['someProperty']; we can combine booleans with&&and||; we can combine strings withstr1 + str2.Likewise, TypeScript has many type operators to transform types into other types. We've already seen some type operators, like
|: if we have typesAandB, we can build a new type out of them by doingA | B. In this lesson, we saw theSomeType['someProperty']type operator that gets the type of a property. We'll see many more type operators in future lessons. Remember that types are a complex language of their own, just like JavaScript is!