Typescript usage log (dry goods)

Typescript usage log

In the past two years, many people have been discussing Typescript. Whether it is in the community or in various articles, it can be seen that the overall positive information is greater than the negative. This article will sort out what I know about Typescript.

This article is mainly divided into 3 parts:

•Typescript basic concepts•Typescript advanced usage•Typescript summary

Typescript basic concepts

As for the definition of the official website, I won’t explain more here, you can check it out on the official website. Typescript design goals [1]

The definition I understand: Give the concept of Javascript type so that the code can find problems before it runs.

What are the types of Typescript

1. Typescript basic types, that is, a single type that can be used directly.

•Number•String•Boolean type•null•undefined•any•unknown•void•object•Enumeration•never

2. A composite type, including multiple types of a single type.

• Array type • Tuple type • Literal type • Interface type

3. What if a type cannot meet the requirements?

•Nullable type, by default any type can be assigned to null or undefined. • Union type, not sure which type is, but can provide several options, such as: type1 | type2. • Cross type, must meet the combination of multiple types, such as: type1 & type2.

Where are the types used

In Typescript, types are usually used in the following situations.

•Used in variables•Used in classes•Used in interfaces•Used in functions

Type used in variable

When using in a variable, just add the type directly after the variable.

let a: number;
let b: string;
let c: null;
let d: undefined;
let e: boolean;
let obj: Ixxx = {
  a: 1,
  b: 2,
};
let fun: Iyyy = () => {};

Type used in class

The usage in a class is similar to that in a variable, except that it provides some static properties, static methods, member properties, and types in the constructor specifically designed for the class.

class Greeter {
    static name:string = 'Greeter'
    static log(){console.log(‘log')}
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}
let greeter = new Greeter("world");

Type used in interface

It is also relatively simple to use in the interface, which can be understood as combining multiple single types.

interface IData {
  name: string;
  age: number;
  func: (s: string) => void;
}

Types used in functions

When using types in functions, they are mainly used to handle function parameters and function return values.

// 函数参数
function a(all: string) {}
// 函数返回值
function a(a: string): string {}
// 可选参数
function a(a: number, b?: number) {}

Typescript advanced usage

The basic usage in Typescript is very simple, and students with js foundation will be able to get started soon. Next, we will analyze the more advanced usage in Typescript to complete more sophisticated type checking.

Advanced usage in classes

The advanced usage in the class mainly has the following points:

•Inheritance•Memory get set•readonly modifier•Public, private, protected modifier•Abstract class abstract

Inheritance and memory are the same as the functions in ES6, so I won't talk about it here, but mainly talk about class modifiers and abstract classes.

The modifiers in the class are the main means to reflect the object-oriented encapsulation. After the attributes and methods in the class are modified by different modifiers, there are different authority divisions, for example:

•Public means that it can be accessed in the current class, subclass, and instance. •Protected means that it can only be accessed in the current class and subclasses. •Private means that it can only be accessed in the current class.

class Animal {
  // 公有,私有,受保护的修饰符
  protected AnimalName: string;
  readonly age: number;
  static type: string;
  private _age: number;
  // 属性存储器
  get age(): number {
    return this._age;
  }
  set age(age: number) {
    this._age = age;
  }
  run() {
    console.log("run", this.AnimalName, this.age);
  }
  constructor(theName: string) {
    this.AnimalName = theName;
  }
}
Animal.type = "2"; // 静态属性
const dog = new Animal("dog");
dog.age = 2; // 给 readonly 属性赋值会报错
dog.AnimalName; // 实例中访问 protected 报错
dog.run; // 正常

The inheritance in the class is also very simple, and the syntax of ES6 is the same.

class Cat extends Animal {
  dump() {
    console.log(this.AnimalName);
  }
}
let cat = new Cat("catname");


cat.AnimalName; // 受保护的对象,报错
cat.run; // 正常
cat.age = 2; // 正常

In object-oriented, there is a more important concept is abstract class, abstract class is used for class abstraction, you can define some public properties, public methods of the class, let the inherited subclass to achieve, or you can implement it yourself.

Abstract classes have the following two characteristics.

• Abstract classes cannot be instantiated directly • Abstract properties and methods in abstract classes must be implemented by subclasses

tip Classic problem: the difference between abstract class interfaces

• The abstract class must be inherited by subclasses, and the interface must be implemented by the class. • Use extends in ts to inherit an abstract class. • Use implements in ts to implement an interface. • Interfaces can only be used for method declarations, and abstract classes can be used for method declarations or method implementations. • Abstract classes are regular, and what is separated is the common part of a category, while interfaces are only abstractions of the same properties and methods, and properties and methods can be unrelated.

The usage of abstract classes is as follows.

abstract class Animal {
  abstract makeSound(): void;
  // 直接定义方法实例
  move(): void {
    console.log("roaming the earch...");
  }
}
class Cat extends Animal {
  makeSound() {} // 必须实现的抽象方法
  move() {
    console.log('move');
  }
}
new Cat3();

Advanced usage in the interface

The advanced usage in the interface mainly has the following points:

• Inheritance • Optional attributes • Read-only attributes • Index type: string and number • Function type interface • Add type to class, constructor type

In addition to the general attributes that can be defined in the interface, optional attributes, index types, etc. can also be defined.

interface Ia {
  a: string;
  b?: string; // 可选属性
  readonly c: number; // 只读属性
  [key: number]: string; // 索引类型
}
// 接口继承
interface Ib extends Ia {
  age: number;
}
let test1: Ia = {
  a: "",
  c: 2,
  age: 1,
};
test1.c = 2; // 报错,只读属性
const item0 = test1[0]; // 索引类型

The interface also supports defining function types and constructor types.

// 接口定义函数类型
interface SearchFunc {
  (source: string, subString: string): boolean;
}
let mySearch: SearchFunc = function (x: string, y: string) {
  return false;
};
// 接口中编写类的构造函数类型检查
interface IClass {
  new (hour: number, minute: number);
}
let test2: IClass = class {
  constructor(x: number, y: number) {}
};

Advanced usage in functions

The advanced usage in the function mainly has the following points:

•Function overloading•this type

Function overloading

Function overloading means that a function can match the corresponding type according to different input parameters.

For example: in the case,  doSomeThing when one parameter is passed, it is prompted as the  number type. If two parameters are passed, the first parameter must be the  string type.

// 函数重载
function doSomeThing(x: string, y: number): string;
function doSomeThing(x: number): string;
function doSomeThing(x): any {}


let result = doSomeThing(0);
let result1 = doSomeThing("", 2);

This type

We all know that this in Javascript can only be judged when it is running, so it is difficult for Typescript to make static judgments. For this Typescript provides us with manual binding of this type, so that we can clarify this In the case of a static type hint.

In fact, in this in Javascript, there are only these five cases:

•Object call, point to the called object•Global function call, point to the window object•call apply call, point to the bound object•dom.addEventListener call, point to this in the dom•arrow function, point to the context of binding

// 全局函数调用 - window
function doSomeThing() {
  return this;
}
const result2 = doSomeThing();


// 对象调用 - 对象
interface IObj {
  age: number;
  // 手动指定 this 类型
  doSomeThing(this: IObj): IObj;
  doSomeThing2(): Function;
}


const obj: IObj = {
  age: 12,
  doSomeThing: function () {
    return this;
  },
  doSomeThing2: () => {
    console.log(this);
  },
};
const result3 = obj.doSomeThing();
let globalDoSomeThing = obj.doSomeThing;
globalDoSomeThing(); // 这样会报错,因为我们只允许在对象中调用


// call apply 绑定对应的对象
function fn() {
  console.log(this);
}
fn.bind(document)();


// dom.addEventListener
document.body.addEventListener("click", function () {
  console.log(this); // body
});

Generic

Generics mean that a type is not defined when it is defined, and it needs to be determined when it is called. It mainly includes the following knowledge points:

• Generic functions • Generic classes • Generic constraints T extends XXX

Let's imagine, if a function outputs the passed parameters directly, how do we write types for it? The incoming parameters can be of any type, do we need to write each type again?

• To use function overloading, you have to write each type again, which is not suitable. • Generic, use a type placeholder T instead, and specify the corresponding type when using it.

// 使用泛型
function doSomeThing<T>(param: T): T {
  return param;
}


let y = doSomeThing(1);


// 泛型类
class MyClass<T> {
  log(msg: T) {
    return msg;
  }
}


let my = new MyClass<string>();
my.log("");


// 泛型约束,可以规定最终执行时,只能是哪些类型
function d2<T extends string | number>(param: T): T {
  return param;
}
let z = d2(true);

In fact, generics are very simple, but many beginners of Typescript find generics difficult. In fact, generics can combine index query operators  keyofand index access operators to  T[k] write code that is difficult to read. Let’s take a look.

// 以下四种方法,表达的含义是一致的,都是把对象中的某一个属性的 value 取出来,组成一个数组
function showKey1<K extends keyof T, T>(items: K[], obj: T): T[K][] {
  return items.map((item) => obj[item]);
}


function showKey2<K extends keyof T, T>(items: K[], obj: T): Array<T[K]> {
  return items.map((item) => obj[item]);
}


function showKey3<K extends keyof T, T>(
  items: K[],
  obj: { [K in keyof T]: any }
): T[K][] {
  return items.map((item) => obj[item]);
}


function showKey4<K extends keyof T, T>(
  items: K[],
  obj: { [K in keyof T]: any }
): Array<T[K]> {
  return items.map((item) => obj[item]);
}


let obj22 = showKey4<"age", { name: string; age: number }>(["age"], {
  name: "yhl",
  age: 12,
});

Type compatibility

Type compatibility is what I think is the most difficult part of Typescript, let's analyze it.

• Object compatibility • Function return value compatibility • Function parameter list compatibility • Function parameter structure compatibility • Compatibility in classes • Compatibility in generics

In Typescript, the compatibility is judged by the structure. If the two structures are the same, they are directly compatible, but if they are inconsistent, Typescript provides us with two compatibility methods:

Take  A = B this expression as an example:

• Covariance means that the structure of B must contain all the structures in A, that is, there can be more attributes in B than A, but not less. • Contraversion is the opposite of covariance, that is: all the attributes in B can be found in A, which can be less than A. • Two-way covariance, that is, there is no rule, B can have more attributes than A, or less than A.

Compatibility among objects

Compatibility among objects uses covariance.

let obj1 = {
  a: 1,
  b: "b",
  c: true,
};


let obj2 = {
  a: 1,
};


obj2 = obj1;
obj1 = obj2; // 报错,因为 obj2 属性不够

Function return value compatible

The compatibility in the return value of the function uses covariance.

let fun1 = function (): { a: number; b: string } {
  return { a: 1, b: "" };
};
let fun2 = function (): { a: number } {
  return { a: 1 };
};


fun1 = fun2; // 报错,fun2 中没有 b 参数
fun2 = fun1;

Compatible number of function parameters

The compatibility of the number of function parameters is inverted.

// 如果函数中的所有参数,都可以在赋值目标中找到,就能赋值
let fun1 = function (a: number, b: string) {};
let fun2 = function (a: number) {};


fun1 = fun2;
fun2 = fun1; // 报错, fun1 中的 b 参数不能再 fun2 中找到

Function parameter compatibility

The function parameters are compatible, and two-way covariance is adopted.

let fn1 = (a: { name: string; age: number }) => {
  console.log("使用 name 和 age");
};
let fn2 = (a: { name: string }) => {
  console.log("使用 name");
};


fn2 = fn1; // 正常
fn1 = fn2; // 正常

Understand the two-way covariance of function parameters

1. Let's think about a function  dog => dog, what are its sub-functions?

Note: If the original function is modified into another function, but its type will not change, ts will still perform type checking according to the type of the original function!

grayDog => grayDog

    •No, if you pass other types of dogs, there is no grayDog method, an error will be reported.

    •grayDog => animal

  • Same as above.

    •animal => animal

  • The return value is incorrect. The return value is always covariant and must be transmitted multiple times.

    •animal => grayDog

  •correct.

Therefore, the function parameter type should be contravariant.

2. Why are the function parameters in Typescript also covariant?

enum EventType { Mouse, Keyboard }
interface Event { timestamp: number; }
interface MouseEvent extends Event { x: number; y: number }


function listenEvent(eventType: EventType, handler: (n: Event) => void) {
  /* ... */
}
listenEvent(EventType.Mouse, (e: MouseEvent) => console.log(e.x + "," + e.y));

In the above code, we pass the mouse type when calling, so in the callback function, we know that the returned parameter must be a MouseEvent type. This is logical, but because the MouseEvent type has more properties than the Event type , So Typescript's parameter types also support covariance.

Compatibility in the class

Compatibility in a class is to compare the structures in two instances, which is a kind of covariance.

class Student1 {
  name: string;
  // private weight:number
}


class Student2 {
  // extends Student1
  name: string;
  age: number;
}


let student1 = new Student1();
let student2 = new Student2();


student1 = student2;
student2 = student1; // 报错,student1 没有 age 参数

It should be noted that the properties and methods in the instance will be affected by the modifiers in the class. If it is a private modifier, it must be ensured that the private modified properties between the two come from the same object. As mentioned above, if the private note is released, compatibility can only be achieved through inheritance.

Compatibility in generics

Compatibility in generics. If T is not used, the two generics are also compatible.

interface Empty<T> {}
let x1: Empty<number>;
let y1: Empty<string>;


x1 = y1;
y1 = x1;

Advanced type

The advanced types in Typescript include: cross type, union type, literal type, index type, mapping type, etc. Here we mainly discuss

• Union type • Mapping type

Union type

The union type means that an object may be one of multiple types, for example, it let a :number | string means that a is either a number type or a string type.

So the question is, how do we determine what type of runtime it is?

Answer: Type protection. Type protection is for the joint type, allowing us to determine the final type from which of the joint types through logical judgment.

There are many ways to determine the type of union:

• typeof • instanceof • in • literal protection, ===, !===, ==, !=• custom type protection, whether there is a property through judgment

// 自定义类型保护
function isFish(pet: Fish | Bird): pet is Fish {
  return (<Fish>pet).swim !== undefined;
}
if (isFish(pet)) {
  pet.swim();
} else {
  pet.fly();
}

Mapping type

The mapping type means that a certain type can be operated to produce another type that meets our requirements:

ReadOnly<T>, change all types in T to read-only. Partial<T>, change all types in T to optional. Exclude<T, U>, remove the types that can be assigned to U from T. Extract<T, U>, extract the types that can be assigned to U in T. NonNullable<T>, remove null and undefined from T. ReturnType<T>Get the return value type of the function. InstanceType<T>Get the instance type of the constructor type.

We can also write custom mapping types.

//定义toPromise映射
type ToPromise<T> = { [K in keyof T]: Promise<T[K]> };
type NumberList = [number, number];
type PromiseCoordinate = ToPromise<NumberList>;
// [Promise<number>, Promise<number>]

Typescript summary

After writing so much, let me talk about some of my views on Typescript.

Typescript advantages

1. Static type checking to find problems early.

2. The type is the document, which is easy to understand and collaborate.

3. Type derivation and automatic completion to improve development efficiency.

4. When an error occurs, the type of problem can be eliminated with a high probability and the bug resolution time can be shortened.

Advantages in actual combat:

1. Discover methods that are deprecated in the es specification, such as Date.toGMTString.

2. Avoid some unfriendly development codes, such as dynamically adding attributes to obj.

3. Vue uses variables. If they are not defined in data, problems will be thrown directly.

Typescript disadvantages

1. Short-term increase in development costs.

2. Some libraries have not yet written type files.

3. Not a complete superset.

Problems in actual combat:

1. There are still some pits that are not easy to solve. After axios has written the interceptor, typescript cannot be reflected in the response.

Reference

• Typescript official website [2] • Deep understanding of Typescript [3]

References

[1] Typescript design goals:  https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
[2]  Typescript official website:  https://www.tslang.cn/
[3]  Deep understanding of Typescript:  https://jkchao.github.io/ typescript-book-chinese/

study Exchange

  • Follow the public account [Frontend Universe], get good article recommendations every day

  • Add WeChat, join the group to communicate


"Watching and forwarding" is the greatest support

Guess you like

Origin blog.csdn.net/liuyan19891230/article/details/108373400