Learning Typescript in 2023 part 3 - Classes and Interfaces

Posted on 23 Feb 2024
Updated on 23 Feb 2024

Classes

Depending on the target you will have different code generated for class.

class Department {
    name: string = "DEFAULT";
    constructor(n: string) {
        this.name = n;
    }
}

let d1 = new Department("Looting");

The above code could be converted to functions if you set the target to ‘ES5’.

Methods

class Department {
    name: string = "DEFAULT";
    constructor(n: string) {
        this.name = n;
    }
    describe() {
        console.log("Describing " + this.name);
	}
}

There is special syntax to specify this for a function inside class

class Department {
    name: string;
    describe(this: Department) { // this should refer to Department kind of object
        console.log("Department is " + this.name);
	}
}

And you can also mark fields as private by using private keyword. While in [S, there is a provision to mark private fields by using #.

class Department {
    private name: string;
    describe(this: Department) {
        console.log("Department is " + this.name);
	}
}

Initialization shortcut

Shortcut declaration of properties in the constructor allows you to skip writing declarations. Do note that this is just a TS feature, the generated JS still has the declarations and assignments.

class Department {
    // private id: string;
    // private name: string;
    constructor(private id: string, public name: string) {
        // You also don't need to assign the values within the constructor
        // this.id = id;
        // this.name = name;
    }
}

readonly

Allows you to create field which gets a value at the time of initialization and cannot be changed later. Again this is a TS feature and not JS.

class Department {
    constructor(private readonly id: string, public name: string) {
    }
}

inheritance

Just like regular inheritance, use extends. Only thing to take care is that super() needs to be called first before operating on any property. If we are using the shortcut syntax in the constructor then it’s automatically handled by TS.

class Department {
    constructor(private readonly id: string, public name: string) {
    }
}

class ChildDepartment extends Department{
    constructor(private readonly id: string, private reports: string[]) {
        super(id, 'Child');
    }
}

protected

Making a field private only allow access to that field in the base class. To access the field in the child class, mark it as protected

class Department {
    protected address: string;
    constructor(private readonly id: string, public name: string) {
    }
}

class ChildDepartment extends Department{
    constructor(private readonly id: string, private reports: string[]) {
        super(id, 'Child');
    }
    changeAddress(add) {
        this.address = add;
	}
}

abstract classes

You can mark methods as abstract to force implementations to write the logic for those methods. Do note that you need to mark the class as abstract too in this case. And if you don’t provide the implementation, you will get TS error.

abstract class Department {
    protected address: string;
    abstract describe();
    constructor(private readonly id: string, public name: string) {
    }
}

class ChildDepartment extends Department{ // Method describe from class Department is not implemented
    constructor(private readonly id: string, private reports: string[]) {
        super(id, 'Child');
    }
    changeAddress(add) {
        this.address = add;
	}
}

Interface

An interface describes the structure of an object. Not sure how it is different from type but will see if it is. You can use readonly to mark fields as to be set only once.

interface Person {
    name: string;
    age: number;
    greet(phrase: string): void;
}

let user1: Person;

user1 = {}; // TS2739: Type '{}' is missing the following properties from type 'Person': name, age, greet

interface vs type

  • interface is clear
  • interface can be used to create classes with multiple inheritance
  • You can extend an interface with other interfaces
interface Greetable {
    name: string;
	greet(phrase: string): void;
}
interface GoodGreetable extends Greetable{
    goodGreet(str: string): void;
}

class Person implements GoodGreetable {
    name: string;
    goodGreet(str: string) {
        console.log("good" + this.name);
    }

    greet() {
        console.log("hi", this.name);
    }
}

Function interface

You can define an interface for functions too below syntax. This now seems to be a stretch. Looks like TS wants to “interfacialize” everything that is there in JS :-P. It would be better to use custom type instead as this interface makes it more complex and confusing.

interface AddFn {
    (a: number, b: number) : number;
}
let add: AddFn;
add = (n1: number, n2: number) => {
    return n1 + n2;
}
add(2,3);

optional properties

interface Person {
    name?: string;
    age: number;

    greet(phrase: string): void;
}

class Human {
    constructor(public name:string, public age:number) {
    }
}

let user1: Person;
user1 = new Human("ola", 34);

Summary

"JS" + "TS" === "Java"

Compatability table

Check what all features are compatible across JS and TS. https://kangax.github.io/compat-table/es6/

Next gen JS

  • Arrow functions
const add = (a:number, b:number) => a+b;