I’ve start learning typescript for project purposes. Typescript is nice, imho. But amount of boilerplate is killing me 😅. Hope it’s benefitial in the end .

Language

Interfaces

This is because when a class implements an interface, only the instance side of the class is checked. Since the constructor sits in the static side, it is not included in this check.

Instead, you would need to work with the static side of the class directly. In this example, we define two interfaces, ClockConstructor for the constructor and ClockInterface for the instance methods. Then, for convenience, we define a constructor function createClock that creates instances of the type that is passed to it:

interface ClockConstructor {
  new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
  tick(): void;
}

function createClock(
  ctor: ClockConstructor,
  hour: number,
  minute: number
): ClockInterface {
  return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
  constructor(h: number, m: number) {}
  tick() {
    console.log('beep beep');
  }
}
class AnalogClock implements ClockInterface {
  constructor(h: number, m: number) {}
  tick() {
    console.log('tick tock');
  }
}

let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);

Because createClock’s first parameter is of type ClockConstructor, in createClock(AnalogClock, 7, 32), it checks that AnalogClock has the correct constructor signature.

Another simple way is to use class expressions:

interface ClockConstructor {
  new (hour: number, minute: number);
}

interface ClockInterface {
  tick();
}

const Clock: ClockConstructor = class Clock implements ClockInterface {
  constructor(h: number, m: number) {}
  tick() {
    console.log('beep beep');
  }
};

Extending interfaces

interface Shape {
  color: string;
}

interface Square extends Shape {
  sideLength: number;
}

let square = {} as Square;
square.color = 'blue';
square.sideLength = 10;

Classes

they have private and protected, and readonly modifier

class Animal {
  private name: string;
  protected color: string;
  readonly numberOfEyes: number;
  constructor(theName: string, numOfEyes: number = 2) {
    this.name = theName;
    this.color = 'Gray';
    this.numberOfEyes = numOfEyes;
  }
}

new Animal('Cat', 2).name; // Error: 'name' is private;
new Animal('Cat', 2).numberOfEyes = 3; // Cannot assign to 'numberOfEyes' because it is a read-only property.

get and set

Notic private _fullName: string;

const fullNameMaxLength = 10;

class Employee {
  private _fullName: string;

  get fullName(): string {
    return this._fullName;
  }

  set fullName(newName: string) {
    if (newName && newName.length > fullNameMaxLength) {
      throw new Error('fullName has a max length of ' + fullNameMaxLength);
    }

    this._fullName = newName;
  }
}

Tuples

Tuples in typescript is an Array with a specific order for elements ;)

const pepsi: [string, boolean, number] = ['brown', true, 40]; //color, carbonated, amount of sugar

or

type Drink = [string, boolean, number];

const pepsi: Drink = ['brown', true, 40]; //color, is carbonated, amount of sugar
const tea: Drink = ['green', false, 0]; //color, is carbonated, amount of sugar

Type guards

If you need to access other properties and methods of the object that is not restricted by the interface or type and stop typescript complaining, type guards wll be handy

//typescript expects `collection` to be either number[] | string

function test(collection: number[] | string) {
  collection[index] = 1; //gives you an error because in case of string []= is not permitted (string is immutable)
  if (collection instance of Array) {
    collection[index] = 1; // is fine because []= is available for arrays and typescript compliler will allow you to do that
  }
  if (typeof collection === 'string') {
    collection.toLowerCase() // typescript aware that we are working with string here and not with array of numberss
  }
}