Add types to your classes

INPUT · Slides

Skip the field declarations

01 / 05

You are writing the same name three times

Look at just the names in the classes you have written so far. name turns up three times. Declared as a field, taken in as a constructor parameter, and put into this.name.

With one thing to hold it is bearable. But at four or five, that is five lines of declarations, five of parameters and five of assignments. Fifteen lines before anything actually happens.

class Cat {  private name: string;  constructor(name: string) {    this.name = name;  }  call(): string {    return this.name;  }}const cat = new Cat("Tama");console.log(cat.call());

Result

Tama

02 / 05

Just put private on the parameter

Put private on a constructor parameter and a field of the same name is made for you, and assigned too. Neither the declaration line nor the this.name = name; line needs writing any more.

What you get is exactly the class above. this.name is a private string and works from the methods as before. This is called constructor shorthand.

class Cat {  constructor(private name: string) {}  call(): string {    return this.name;  }}const cat = new Cat("Tama");console.log(cat.call());

Result

Tama

03 / 05

A parameter with no modifier does not become a field

Here is the crux. What becomes a field is only a parameter with a modifier on it. A parameter with nothing on it stays an ordinary parameter, usable only inside the constructor as before.

So forget the private, as below, and you are stopped with "there is no such thing as this.name". Remember this error when you leave it off.

class Cat {  constructor(name: string) {}  call(): string {    return this.name;  }}

Result

Type error: Property 'name' does not exist on type 'Cat'.

04 / 05

readonly works the same way

private is not the only one you can put there. readonly works, and so does private readonly with both together. Whatever modifier you write becomes the field's modifier.

Once there are two or more parameters, put them one per line, stacked. They stay readable on a 375px screen, and adding something to hold later is one extra line.

class Member {  constructor(    readonly id: number,    private points: number,  ) {}  look(): number {    return this.points;  }}const k = new Member(7, 30);console.log(k.id);console.log(k.look());

Result

7
30

05 / 05

Nearly every class out there looks like this

What is nice is not only that there are fewer lines. The list of what it holds matches the list of constructor parameters exactly, so whoever reads it need not look up and down comparing. That is why nearly every class you meet in the wild is written this way.

What you get is exactly the class you had before. Which also means that showing an instance directly still reveals the private fields, just as it did.

Right, have a go. Delete the declaration and the assignment, and add one word to the parameter.

class Fruit {  constructor(    private name: string,    private price: number,  ) {}}const k = new Fruit("apple", 120);console.log(k);

Result

{ name: "apple", price: 120 }