Exploring Code #64: Type Annotations & Aliases for Nested Objects in TS

Exploring Code #64: Type Annotations & Aliases for Nested Objects in TS

I'm taking Colt Steele's Mastering Typescript course and I want to spend some time working out the basics of defining object shapes and type aliases because it's an important concept.

So for a basic object, you can define an alias and a type like this:

type Person = {
    first: string,
    last: string,
    age: number
}        

And instead of defining the shape within a printName function, you can substitute the shape in the parameter definitions.

So, this:

type Person = {
    first: string,
    last: string,
    age: number
}

function printName(person: Person) :Person {
    return {first: person.first, last: person.last, age: person.age}
}

printName({ first: "Hank", last: "Hill", age: 100 })
        

Instead of this, where you have that really bloated function argument going on:

function printName(person: { first: string, last: string, age: number }) {
  return {first: person.first, last: person.last, age: person.age}
}

printName({ first: "Hank", last: "Hill", age: 100 })        

And compiled down, it just looks like a normal function that returns an object.

Article content

But where this really comes in handy with cleaning up your typing in parameters is when we get into more complicated, nested data shapes.

Now Let's Tackle Nested Objects

This is straight up ripped off from Colt's course with some slight modifications. But it really stuck with me as an explanation, so definitely check out his course because he has a knack for teaching.

So, let's define a Song type.

type Song = {
 title: string,
 artist: string,
 numStreams: number,
 albumInfo: {recordLabel: string, yearReleased: number, albumName:    string}
    }
        

Where this diverges from the previous example is that nested objected within the albumInfo property

albumInfo: {recordLabel: string, yearReleased: number, albumName:           

Using Colt's calculatePayout function, you can see how this structure would be handled with typing.

function calculatePayout(song:Song) :number {
return song.numStreams * 0.0033;
}        

Right?

So instead of typing out that huge nested object within the function parameters, we are just referencing the type alias that we set up before.

The 'song' parameter is just simply any other object key we'd pass into a regular function. And the UPPERCASE 'Song' is the actual shape that TypeScript will check against.

And the :number is just a way of signifying that the value that is gonna be returned is a number. TypeScript is smart enough to infer it, but it's always nice to leave that designation.

Because, after all, if you are 50 lines of code deep into a function. And multiple things are being returned, it's nice to know right away that we are looking for a number specifically.

So, doing the same thing with a printSong function:

function printSong(song: Song): void {
    console.log(`${song.title} by ${song.artist} 
has ${song.numStreams} total streams. The album this song is from is ${song.albumInfo.albumName} 
and it was released in ${song.albumInfo.yearReleased} `)
}

const theSong: Song = {
    title: "What a Horrible Night to Have a Curse",
    artist: "The Black Dahlia Murder",
    numStreams: 1000000000,
    albumInfo: {
        recordLabel: "Sumerian Records",
        yearReleased: 2007,
        albumName: "Nocturnal"
    }
}

printSong(theSong)        

And if you compile with TS and then run with Node, you get the correct output:

Article content


To view or add a comment, sign in

More articles by Gabriel Thurau

Insights from the community

Others also viewed

Explore topics