Execute Program

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'Pass Icon
  • We can do the same with object types themselves. If Album is the type {name: string, copiesSold: number}, then Album['name'] is the type string.

  • >
    type Album = {
    name: string
    lengthMinutes: number
    };

    type AlbumName = Album['name'];

    const albumName: AlbumName = 'Kind of Blue';
    albumName;
    Result:
    'Kind of Blue'Pass Icon
  • 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 AlbumName type directly, rather than using the type indexing feature.

  • >
    type AlbumName = string;

    type Album = {
    name: AlbumName
    copiesSold: number
    };

    const albumName: AlbumName = 'Kind of Blue';
    albumName;
    Result:
    'Kind of Blue'Pass Icon
  • 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 ConnectionOptions type:

  • >
    type ConnectionOptions = {
    host: string // the IP address or hostname of the server
    port: number
    credentials: {
    username: string
    password: string
    }
    };
  • Now we want to write a buildConnectionOptions function as part of our database setup code. It should take a host, a port, and credentials. But the authors of our database library didn't give the credentials type a name! As we can see in the definition above, that object type is defined inline inside of the ConnectionOptions type.

  • We could choose to duplicate the credentials type 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 the credentials type 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 ConnectionOptions type. You won't need to change any other part of the code.

    type ConnectionOptions = {
    host: string
    port: number
    credentials: {
    username: string
    password: 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'}}Pass Icon
  • 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 as Album.name, but we can't! This feature only works with square brackets, not with the dot operator.

  • >
    type Album = {
    name: string
    copiesSold: 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 number and string. 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 with obj['someProperty']; we can combine booleans with && and ||; we can combine strings with str1 + str2.

  • Likewise, TypeScript has many type operators to transform types into other types. We've already seen some type operators, like |: if we have types A and B, we can build a new type out of them by doing A | B. In this lesson, we saw the SomeType['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!