Class usage in TypeScript

Public, private and protected

TypeScript uses three types of Access Modifiers: public, private, and protected.

A public property or method is public and can be accessed anywhere. By default, all properties and methods are public. A private property or method is private and cannot be accessed outside the class in which it is declared. It is similar to private, except that it is also accessible in subclasses

class Animal {
  public name;
  public constructor(name) {
    this.name = name;
  }
}

let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';
console.log(a.name); // Tom
Copy the code

In the example above, the name is set to public, so direct access to the name attribute of the instance is allowed.

We want properties that are not directly accessible, so we can use private:

class Animal { private name; public constructor(name) { this.name = name; } } let a = new Animal('Jack'); console.log(a.name); // Jack a.name = 'Tom'; / / index. Ts (9, 13) : error TS2341: Property 'name' is private and only accessible within class 'Animal'. // index.ts(10,1): error TS2341: Property 'name' is private and only accessible within class 'Animal'.Copy the code

There is nothing in TypeScript compiled code that restricts the external accessibility of private properties.

The compiled code for the above example is:

var Animal = (function () { function Animal(name) { this.name = name; } return Animal; }) (); var a = new Animal('Jack'); console.log(a.name); a.name = 'Tom';Copy the code

Properties or methods that use private decoration are also not accessible in subclasses:

class Animal { private name; public constructor(name) { this.name = name; } } class Cat extends Animal { constructor(name) { super(name); console.log(this.name); }} // index.ts(11,17): error TS2341: Property 'name' is private and only accessible within class 'Animal'.Copy the code

If protected, access is allowed in subclasses:

class Animal { protected name; public constructor(name) { this.name = name; } } class Cat extends Animal { constructor(name) { super(name); console.log(this.name); }}Copy the code

When the constructor is private, the class is not allowed to be inherited or instantiated:

class Animal {
  public name;
  private constructor(name) {
    this.name = name;
  }
}
class Cat extends Animal {
  constructor(name) {
    super(name);
  }
}

let a = new Animal('Jack');

// index.ts(7,19): TS2675: Cannot extend a class 'Animal'. Class constructor is marked as private.
// index.ts(13,9): TS2673: Constructor of class 'Animal' is private and only accessible within the class declaration.
Copy the code

When the constructor is protected, the class is only allowed to be inherited:

class Animal {
  public name;
  protected constructor(name) {
    this.name = name;
  }
}
class Cat extends Animal {
  constructor(name) {
    super(name);
  }
}

let a = new Animal('Jack');

// index.ts(13,9): TS2674: Constructor of class 'Animal' is protected and only accessible within the class declaration.
Copy the code

Parameter properties

Modifiers and readonly can also be used in constructor arguments, equivalent to defining and assigning a value to the property in the class, making code more concise.

class Animal { // public name: string; public constructor(public name) { // this.name = name; }}Copy the code

readonly

Read-only attribute keywords, which are allowed only in attribute declarations or index signatures or constructors.

class Animal {
  readonly name;
  public constructor(name) {
    this.name = name;
  }
}

let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';

// index.ts(10,3): TS2540: Cannot assign to 'name' because it is a read-only property.
Copy the code

Note that if readonly and other access modifiers exist at the same time, they need to be written after them.

class Animal { // public readonly name; public constructor(public readonly name) { // this.name = name; }}Copy the code

An abstract class

Abstract is used to define abstract classes and their abstract methods.

What is an abstract class?

First, abstract classes are not allowed to be instantiated:

abstract class Animal {
  public name;
  public constructor(name) {
    this.name = name;
  }
  public abstract sayHi();
}

let a = new Animal('Jack');

// index.ts(9,11): error TS2511: Cannot create an instance of the abstract class 'Animal'.
Copy the code

In the example above, we defined an abstract class Animal and an abstract method sayHi. An error was reported while instantiating the abstract class.

Second, abstract methods in an abstract class must be implemented by a subclass:

abstract class Animal { public name; public constructor(name) { this.name = name; } public abstract sayHi(); } class Cat extends Animal { public eat() { console.log(`${this.name} is eating.`); } } let cat = new Cat('Tom'); / / index. Ts (9, 7) : error TS2515: Non-abstract class 'Cat' does not implement inherited abstract member 'sayHi' from class 'Animal'.Copy the code

In the example above, we defined a class Cat that inherited from Animal, but did not implement the abstract method sayHi, so the compiler reported an error.

Here’s an example of how to use an abstract class correctly:

abstract class Animal {
  public name;
  public constructor(name) {
    this.name = name;
  }
  public abstract sayHi();
}

class Cat extends Animal {
  public sayHi() {
    console.log(`Meow, My name is ${this.name}`);
  }
}

let cat = new Cat('Tom');
Copy the code

In the example above, we implemented the abstract method sayHi, and it compiled.

It is important to note that even if the abstract method, TypeScript compilation of results, this class will still exist, compile the results of the above code is:

var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : ((__.prototype = b.prototype), new __()); }; var Animal = (function () { function Animal(name) { this.name = name; } return Animal; }) (); var Cat = (function (_super) { __extends(Cat, _super); function Cat() { _super.apply(this, arguments); } Cat.prototype.sayHi = function () { console.log('Meow, My name is ' + this.name); }; return Cat; })(Animal); var cat = new Cat('Tom');Copy the code

The type of the class

Attaching TypeScript types to classes is simple, just like an interface:

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  sayHi(): string {
    return `My name is ${this.name}`;
  }
}

let a: Animal = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
Copy the code

Class implementation interface

interface Alarm { alert(): void; } class Door { } class SecurityDoor extends Door implements Alarm { alert() { console.log('SecurityDoor alert'); } } class Car implements Alarm { alert() { console.log('Car alert'); }}Copy the code

A class can implement multiple interfaces:

interface Alarm { alert(): void; } interface Light { lightOn(): void; lightOff(): void; } class Car implements Alarm, Light { alert() { console.log('Car alert'); } lightOn() { console.log('Car light on'); } lightOff() { console.log('Car light off'); }}Copy the code

Interface Inheritance interface

The relationship between interfaces can be inherited:

interface Alarm {
    alert(): void;
}

interface LightableAlarm extends Alarm {
    lightOn(): void;
    lightOff(): void;
}
Copy the code

This makes sense. LightableAlarm inherits from Alarm, and in addition to having the alert method, it also has two new methods, lightOn and lightOff.

Interface inheritance class

Interfaces cannot inherit classes in common object-oriented languages, but they do in TypeScript:

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};
Copy the code

Why does TypeScript support interface inheritance?

In fact, when we declare class Point, in addition to creating a class named Point, we also create a type named Point (the type of the instance).

So we can use Point as a class (create an instance of it with new Point) :

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

const p = new Point(1, 2);
Copy the code

You can also use Point as a type (using: Point for the type of the argument) :

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

function printPoint(p: Point) {
    console.log(p.x, p.y);
}

printPoint(new Point(1, 2));
Copy the code

This example can actually be equivalent to:

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

interface PointInstanceType {
    x: number;
    y: number;
}

function printPoint(p: PointInstanceType) {
    console.log(p.x, p.y);
}

printPoint(new Point(1, 2));
Copy the code

In the example above, our newly declared PointInstanceType is equivalent to the Point type created when we declared class Point.

So going back to the Point3d example, it’s easy to see why TypeScript supports interface inheritance classes:

class Point { x: number; y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } } interface PointInstanceType { x: number; y: number; } interface Point3d extends PointInstanceType Interface Point3d extends Point {z: number; } let point3d: Point3d = {x: 1, y: 2, z: 3};Copy the code

When we declare interface Point3d extends Point, what Point3d actually inherits is the type of an instance of the Point class.

When you declare a Point class, you create a Point type that contains only its instance properties and instance methods:

Class Point {/** static origin = new Point(0, 0); /** static distanceToOrigin(p: Point) {return math.sqrt (p.x * p.x + p.y * p.y); } /** instance attribute, x axis value */ x: number; */ y: number; /** constructor */ constructor(x: number, y: number) {this.x = x; this.y = y; } /** Instance method, print this point */ printPoint() {console.log(this.x, this.y); } } interface PointInstanceType { x: number; y: number; printPoint(): void; } let p1: Point; let p2: PointInstanceType;Copy the code

The last type Point in the above example is equivalent to the type PointInstanceType.

Similarly, when an interface inherits from a class, it inherits only its instance properties and instance methods.

reference

Introduction to TypeScript – ts.xcatliu.com/

TypeScript website – www.typescriptlang.org/