Don't overlook the importance of functions and classes in TypeScript

The last article summarized Type annotation for TypeScript , let's talk about equally important functions and classes

function

The following declares a function type. The type alias is defined by type. void indicates that there is no return value

type fnType = () => void;
As a parameter

A function can be passed as an argument to another function

type fnType = () => void;
function foo(fn: fnType) {
  fn();
}
function bar() {
  console.log("bar");
}
foo(bar);      // bar

The difference from js code is that the passed in parameters need to define type annotations

Define function

When defining a function, you need to specify a type annotation for the input parameter. If the return value can be derived by yourself, it can also be left blank, such as the add and minus functions, but it must be written as a parameter, such as the input parameter fn in the calc function

function add(num1: number, num2: number): number {
  return num1 + num2;
}
function minus(num1: number, num2: number): number {
  return num1 - num2;
}
function calc(
  num1: number,
  num2: number,
  fn: (num1: number, num2: number) => void
) {
  console.log(fn(num1, num2));
}

calc(30, 20, add);      // 50
calc(30, 20, minus);      // 10
Type of function parameter

The function in ts will specify the type and number of parameters. When the number is uncertain, you can use optional types, remaining parameters and default values

optional type
The optional type is equivalent to the joint type of the defined type and undefined, so there are three options for parameters: pass in the type, not pass in or undefined

function foo(x: number, y?: number) {
  console.log(x, y);
}
foo(1, 2);      // 1 2
foo(3);      // 3 undefined
foo(4, undefined);      // 4 undefined

Parameter defaults
If the parameter has a default value, it is called an optional type. However, the parameter with a default value is best placed after the required parameter

function baz(x: number = 20, y: number) {
  console.log(x, y);
}
baz(10, 20);      // 10 20
baz(undefined, 20);      // 20 20

Residual parameters
The remaining parameters must be passed after the parameters

function sum(num: number, ...args: number[]) {
  console.log(num, args);
}
sum(10);      // 10 []
sum(10, 20);      // 10 [20]
sum(10, 20, 30);      // 10 [20, 30]
Default derivation of this

This and ts defined in the method of object can be deduced automatically, but this in independent function cannot be deduced.

You must define the type of this in a stand-alone function

type ThisType = { name: string };
const eating = function (this: ThisType) {
  console.log(this.name + " eating~");
};

const person = {
  name: "kiki",
  eating,
};
person.eating()
function overloading

Function overloading refers to multiple processing methods defined with the same function name and different number or types of parameters.

For example, we need to splice strings or sum numbers. Although we know that the + sign can be used for strings and numbers, in ts code with strict type detection, this writing and compilation does not pass. We need to use [type reduction] to reduce the judgment of types before processing.

By combining types, the more possible parameter combinations are, the more if statements are needed to judge, and the return value type of the function is also unknown. In this case, you can use function overload

In ts, the overloaded function of function name and parameter type is defined, and then the specific implementation is defined through the implementation function. It will be called in the overloaded function according to the data type, and then execute the code of the realization function.

function add(x: number, y: number): number;
function add(x: string, y: string): string;

function add(x: any, y: any) {
  return x + y;
}
console.log(add(1, 2));
console.log(add("a", "b"));

If the parameters passed are different from those defined in the overloaded function, they cannot be compiled.

class

initialization

The variables defined in the class need to be initialized. You can assign values when defining the class or operate through the constructor

class Person {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
const person = new Person("alice", 20);
inherit

ts and js are the same. They are inherited through extensions. When using parent parameters and methods, the super keyword is used.

class Person {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

class Student extends Person {
  sno: number;
  constructor(name: string, age: number, sno: number) {
    super(name, age);
    this.sno = sno;
  }
}
const student = new Student("alice", 20, 1);
polymorphic

Using polymorphism can write more general code. If you want to realize polymorphism, you must first have inheritance, and the parent class reference points to the child class object.

class Animal {
  action() {
    console.log("animal action");
  }
}
class Dog extends Animal {
  action() {
    console.log("dog running");
  }
}
class Fish extends Animal {
  action() {
    console.log("fish swimming");
  }
}
function doAction(animals: Animal[]) {
  animals.forEach((animal) => {
    animal.action();
  });
}
doAction([new Dog()]);
doAction([new Dog(), new Fish()]);
doAction([new Dog(), new Fish(), new Animal()]);

This is equivalent to const animal1: Animal = new Dog(). It looks like an animal object, but it is actually a Dog object. The parent class reference here points to a child class object, so the last execution is the method of the Dog object

member qualifier character

There are three types of member modifiers

  • Public means common and can be seen everywhere. When no member modifier is defined, it defaults to public
  • Private is private and can only be accessed in the class
  • protected means that the class itself and its subclasses can be accessed

public

class Person {
  public username: string = "alice";
  getName() {
    return this.username;
  }
}
const person = new Person();
console.log(person.username);

private
Variables modified by private are also inaccessible on the instance object.

protected
Variables modified by protected are also inaccessible on the instance object.

readonly

readonly means that the property is read-only. After initialization, it cannot be modified in any way, whether inside the class or modifying the instance object. However, when it is an object, it is possible to modify the property of the object as long as its memory address is not changed.

Accessor

The accessor provides get/set methods for private properties, allowing us to get / modify private properties outside the class

class Person {
  private _name: string;
  constructor(newName: string) {
    this._name = newName;
  }
  get name() {
    return this._name;
  }
  set name(newName) {
    if (newName) this._name = newName;
  }
}

const person = new Person("alice");
console.log(person.name);
person.name = "kiki";
console.log(person.name);
person.name = "";
console.log(person.name);

The function of interception / judgment can be achieved by modifying private attributes through get/set attributes

Static member

Static members are defined by the static keyword. The attributes defined by static are defined in the class itself and can only be accessed by itself. They cannot be accessed inside the class and the instance object.

abstract class

When defining many general calling interfaces, we usually let the caller pass in the parent class to realize a more flexible calling method through polymorphism.

However, the parent class itself may not need to implement some methods, so the methods defined in the parent class can be defined as abstract methods.

abstract class Shape {
  abstract getArea(): void;
}

class Circle extends Shape {
  private radius: number;
  constructor(radius: number) {
    super();
    this.radius = radius;
  }
  getArea() {
    return this.radius * this.radius * 3.14;
  }
}

class Rectangle extends Shape {
  private width: number;
  private height: number;
  constructor(width: number, height: number) {
    super();
    this.width = width;
    this.height = height;
  }
  getArea() {
    return this.width * this.height;
  }
}

function calcArea(shape: Shape) {
  return shape.getArea();
}

console.log(calcArea(new Circle(3)));
console.log(calcArea(new Rectangle(2, 6)));

Abstract methods and methods are modified by abstract, and there are two rules when defining abstract classes:

  • Abstract methods must be implemented in subclasses
  • Abstract classes cannot be instantiated

Type of class

Class itself can also be used as a data type and as a type annotation.

class Person {
  name: string = "alice";
  eating() {}
}
const person: Person = {
  name: "kiki",
  eating() {
    console.log("i am eating");
  },
};
function printPerson(person: Person) {
  console.log(person.name);
}

printPerson(new Person());
printPerson(person);
printPerson({ name: "macsu", eating() {} });

The type of class can be used as long as it conforms to the format of the class

Functions and classes are very important in JS and TS, which can help developers better standardize the code during development and reduce online failures~

The above is about TypeScript functions and classes. There are still many places for developers to master about js and ts. you can see other blog posts I wrote and keep updating~

Added by jfontein on Sun, 06 Mar 2022 15:57:47 +0200