TypeScript Basic Tutorial

1. Introduction to TypeScript

1. Characteristics

  • Typescript is a language built on top of JavaScript
  • A superset of JavaScript
  • Cannot be executed directly by JS parser
  • Extends JS, and adds types

The relationship between TS, ES6, and JS:
insert image description here
TS online compiler

2. Build the environment

  1. Download Node

Download address: https://nodejs.org/en/download/

  1. Install Node

  2. Install typescript globally using npm

npm i typescript -g

Verify that the installation was successful:tsc -v

  1. Create a ts file

  2. Use tsc to compile the ts file

  • enter the command line
  • Enter the directory where the ts file is located
  • Excuting an order:tsc greeter.ts

After executing the command, a compiled file will appear in the same directory greeter.js.

  1. run js

node greeter.js

  1. Simplify the ts running process

    Use ts-node, you can run the ts file directly. It needs to be installed globally first npm i -g ts-node. Then execute ts-node greeter.ts.

    When ts-node is executed, it converts the ts file into js internally, and then executes the js code;

3. Description of common methods

3.1 keyof

keyofIt Object.keysis slightly similar to , except that keyofit takes the key of the object type , and after the key keyofis taken it will be saved as a joint type .

type Point = {
    
     x: number; y: number };
type P = keyof Point;
//type P = keyof Point   		等效

type Arrayish = {
    
     [n: number]: unknown };
type A = keyof Arrayish;
//type A = number 				等效

type Mapish = {
    
     [k: string]: boolean };
type M = keyof Mapish;
//type M = string | number		等效

Combined class use:

type Person = {
    
    
    name: string
    age: number
    add: string
}
class Student {
    
    
    constructor(private info: Person) {
    
     }
    getInfo<T extends keyof Person>(key: T): Person[T] {
    
    
        return this.info[key];
    }
}
let student = new Student({
    
     name: "IT飞牛", age: 19, add: "上海" });
console.log(student.getInfo("name"));       //打印:IT飞牛

Use with generic classes:

class Student<K> {
    
    
    constructor(private info: K) {
    
     }
    getInfo<T extends keyof K>(key: T): K[T] {
    
    
        return this.info[key];
    }
}

let student = new Student({
    
     name: "IT飞牛", age: 19 });
console.log(student.getInfo("name"));       //打印:IT飞牛

3.2 in keyof

inIt can be understood that for ... init means traversing each type from keyof Tit , using the above example, it is the type of "a", and"b" . "c"Note that they are not stringtype , but literal stringtype, which is a specific string type. For example, a variable of "a"type can only be "a"a value.

Represents a string literal type, often used in mapping types

type Props = {
    
     a: number; b: number; c: number };
// 映射类型:根据对象类型来创建
type Type3 = {
    
     [key in keyof Props]: number };

3.2 extends keyof

K extend keyof TIndicates Kthat is Ta subtype of , here is a type constraint declaration. For example type T = "a" | "b" | "c";, then Kcan be "a", can also be "a" | "c"or "a" | "b" | "c"etc.;

Represents a sub-joint type, often used to define a sub-joint type

class Student<K> {
    
    
    constructor(private info: K) {
    
     }
    getInfo<T extends keyof K>(key: T): K[T] {
    
    
        return this.info[key];
    }
}

Two, TypeScript commonly used types

TypeScript is a superset of JS, TS provides all the functions of JS, and additionally adds a type system;

  • All js code is ts code
  • js has types (for example: number, string, ...), but js does not check whether the type of the variable changes. And TS will check. The main advantage of the TypeScript type system: It can explicitly flag unexpected behavior in the code, thereby reducing the possibility of errors.

1. Type annotations

let age:number=18;

:numberType annotations are in the code . The role is to add type constraints to variables. Whatever type is agreed upon, only the value of that type can be assigned to the variable, otherwise, an error will be reported.

2. Overview of common basic types

Common basic types in TS can be subdivided into two categories: JS existing types and TS new types .

  • JS already has types:

    • Primitive types: string, number, boolean, null, undefined, symbol
    • Object type: object (array, object, function, etc.)
  • TS new type

    • Union type, custom type (type alias), interface, tuple, literal type, enumeration, any, etc.

2.2 Primitive types

Primitive types: string, number, boolean, null, undefined, symbol

Features: Simple. These types are written exactly according to the names of the types in js.

let flag: boolean = true;
let myname: string = "IT飞牛";
let num: number = 100;

2.3 Array types

Two ways of writing array types:

let numbers:number[]=[1,2,3];
let strings:Array<string>=["a","b","c"];

If there are both numbertypes and stringtypes in the array, how should the type of the array be written?

let arr:(number|string)[]=[1,"a",3,"b"];

| (vertical bar) is called a joint type in TS (a type composed of two or more other types, indicating that it can be any of these types).

2.4 Type aliases

Type Alias ​​(Custom Type): Alias ​​any type.

Usage scenario: When the same type (complex) is used multiple times, the type alias can be used to simplify the use of this type.

type CustomArray=(number|string)[];
let arr1:CustomArray=[1,"a",3,"b"];
let arr2:CustomArray=["x","y",6,7];

explain:

  • Use the type keyword to create type aliases
  • A type alias (for example, CustomArray here), can be any legal variable name.
  • After creating a type alias, you can directly use the type alias as the type annotation of the variable.

2.5 Function types

The type of a function actually refers to: the type of function parameters and return value.

There are two ways to specify a type for a function:

  • Separately specify the type of parameters and return values
function add(num1: number, num2: number): number {
    
    
    return num1 + num2;
}
const add = (num1: number, num2: number): number => {
    
    
    return num1 + num2;
}
  • Specify the type of parameters and return value at the same time
const add : (num1: number, num2: number) => number = (num1, num2) => {
    return num1 + num2;
}

When a function is an expression, you can add a type to the function through a syntax similar to the arrow function form. This form works only with function expressions.

If the function does not return a value, then the function return value type is: void.

function greet(name:string):void{
    
    
    console.log("Hello",name);
}

When using a function to implement a function, parameters can be passed or not. In this case, optional parameters are used when specifying a type for a function parameter. For example, slicethe method of the array, can slice()also be slice(1), can also be slice(1,3).

function mySlice(start?:number,end?:number):void{
    
    
    console.log("起始索引:",start,"结束索引:",end);
}

Optional parameters: Add ? after the name of optional and non-optional parameters (question mark)

Optional parameters can only appear at the end of the parameter list, that is to say, mandatory parameters cannot appear after optional parameters.

Functions with rest parameters:

function add(...nums: number[]) {
    
    
    let sum = 0;
    sum = nums.reduce((a, b) => {
    
    
        return a + b;
    })
    console.log(sum);
}

add(1, 2, 3);   //打印:6

2.6 Object types

Objects in JS are composed of properties and methods, and the type of objects in TS is to describe the structure of the object (what types of properties and methods are there).

The writing method of object type:

let person: {
    
     name: string; age: number; sayHi(): void } = {
    
    
    name: 'IT飞牛',
    age: 19,
    sayHi() {
    
     }
}
  • Use directly {}to describe object structures. The form an attribute takes 属性名:类型; 方法名():返回值类型the form a method takes.
  • If the method has parameters, specify the parameter type in parentheses after the method name (for example: greet(name:string):void).
  • When specifying multiple property types of an object in one line of code, use (semicolon) to separate them.
  • If a line of code specifies only one attribute type (multiple attribute types are separated by newlines), you can remove (semicolon)
  • The type of the method can also use the arrow function form (for example: {sayHi:()=>void})

The properties or methods of an object can also be optional, and optional properties are used at this time.

For example: when we use it axios({...}), if we send a GET request, the method attribute can be omitted.

function myAxios(config:{
    
    url:string;method?:string}){
    
    
    console.log(config);
}

The syntax of optional attributes is the same as that of optional parameters of functions, which are represented by (question mark).

2.7 Interface

When an object type is used multiple times, an interface is generally used (interface)to describe the type of object to achieve the purpose of reuse.

  • Use interfacekeywords to declare interfaces
  • Interface name (for example: here IPerson), can be any legal variable name
  • After declaring an interface, use the interface name directly as the type of the variable.
  • Because there is only one attribute type per line, there is no (semicolon) after the attribute type
interface IPerson{
    
    
    name:string
    age:number
    sayHi():void
}
let person:IPerson={
    
    
    name:"IT飞牛",
    age:19,
    sayHi(){
    
    }
};

Comparison of interface and type:

The same point: both can specify types for objects

difference:

  • Interface: Types can only be specified for objects.
  • Type aliases: Not only can you specify types for objects, but you can actually specify aliases for any type.
interface IPerson {
    name: string
    age: number
    sayHi(): void
}
type IPerson{
    name: string
    age: number
    sayHi(): void
}
type NumStr = number | string

If there are the same properties or methods between the two interfaces, the public properties or methods can be extracted and reused through inheritance.

For example, both interfaces have x、ythese two attributes, repeating these two times will be very complicated.

//改造前
interface Point2D {
    
     x: number, y: number }
interface Point3D {
    
     x: number, y: number, z: number }
		
//改造后
interface Point2D {
    
     x: number, y: number }
interface Point3D extends Point2D {
    
     z: number }
  • Interface inheritance extendsis achieved using the (inherit) keyword .Point3DPoint2D
  • After inheritance, Point3Dthere are Point2Dall properties and methods (at this time, Point3Dthere are x、y、zthree properties at the same time).

2.8 tuples

On the map, location information is marked using latitude and longitude coordinates.

An array can be used to record the coordinates. Then, there are only two elements in the array, and both elements are of numeric type.

let position:number[]=[39.3213,116.3123];

Disadvantages of use number[]: not rigorous, because any number of numbers can appear in this type of array. A better way: Tuple

A tuple type is another type of array that knows exactly how many elements it contains, and which type a particular index corresponds to.

let position: [number, number] = [1.1, 2.3];

2.9 Type inference

In TS, where the type is not clearly indicated, TS's type inference mechanism will help provide the type.

In other words: Due to the existence of type inference, type annotations can be omitted in these places!

There are two common scenarios where type inference occurs:

  • When declaring a variable and initializing
//鼠标移入age,TS自定推断出变量age为number类型
let age=18;
  • When determining the return value of a function
//鼠标移入add,系统自动推断函数返回值是number类型
function add(num1:number,num2:number){
    
    
    return num1+num2;
}

In both cases, type annotations can be omitted!

Recommendation: omit the type annotation where it can be omitted (lazy, make full use of the ability of TS type inference to improve development efficiency)

Tip: If you don’t know the type, you can place the mouse on the variable name and use VSCode’s prompt to view the type.

2.10 Type Assertions

Sometimes you will be more specific about the type of a value than TS. At this time, you can use type assertion to specify a more specific type.

<a href="https://blog.csdn.net/bobo789456123" id="link">IT飞牛</a>

//此时类型推断会识别alink的类型为:HTMLElement
const alink=document.getElementById("link")

alinkAt this time, the type that type inference will recognize is HTMLElementthat this type only contains attributes or methods common to all tags, and does not contain atag-specific hrefattributes.

Therefore, this type is too broad to operate on attributes or methods specific to hrefsuch atags.

//这两个写法等效
const alink = document.getElementById("link") as HTMLAnchorElement
const alink=<HTMLAnchorElement>document.getElementById("link")
  • Use askeywords to implement type assertions
  • The type following the keyword asis a more specific type ( subtype of yes. HTMLAnchorElement)HTMLElement

2.11 Literal types

Think about the code, what are the types of the two variables?

let str1="Hello feiniu";
const str2="Hello IT";

Through the TS type inference mechanism, the answer that can be obtained:

  • The variable str1 is of typestring
  • The variable str2 is of typeHello IT

str2 is a constant, its value cannot be changed Hello IT, so its type is Hello IT.

Note: here Hello ITis a literal variable. That is to say, a specific string can also be used as a type in TS. In addition to strings, any JS literal (such as: object, number, etc.) can be used as a type.

**Common usage scenarios:** Used to represent a list of explicit set of optional values. Often used with union types.

function changeDirection(direction:"up"|"down"|"left"|"right"){
    
    
    console.log(direction);
}

Note: directionThe value of the parameter can only be up/down/left/rightany one of them.

Advantages: Compared with stringtypes, using literal types is more precise and rigorous.

2.12 Enumeration

The function of the enumeration is similar to the function of the combination of literal type + union type, and can also represent a clear set of optional values.

Enum: Defines a set of command constants. It describes a value, which can be one of these named constants.

2.12.1 Numeric enumerations

//使用联合类型+字面量类型
function changeDirection1(direction: Direction1) {
    
    
    console.log(direction);
}
type Direction1 = "up" | "down" | "left" | "right";
changeDirection1("up");

// 使用枚举
function changeDirection2(direction: Direction2) {
    
    
    console.log(direction);
}
enum Direction2 {
    
     Up, Down, Left, Right };
changeDirection2(Direction2.Up);
  • enumDefining enumerations using keywords
  • Convention enumeration name, value in the enumeration starts with uppercase and lowercase letters
  • Multiple values ​​in an enumeration are separated by (commas)
  • After defining the enumeration, use the enumeration name directly as the type annotation

Direction2Each enumeration value is not specified in, and the default is a numeric enumeration. For example, the following Direction2.Upis passed in as an actual parameter, and its value is 0. That is to say, the enumeration members have values, and the default value is an auto-increment value from 0 .
insert image description here
We can also initialize values ​​in enums.

function changeDirection2(direction: Direction2) {
    
    
    console.log(direction);
}
enum Direction2 {
    
     Up = 2, Down = 4, Left = 6, Right };
changeDirection2(Direction2.Up);    //值为2
changeDirection2(Direction2.Right); //值为7

2.12.2 String enumeration

The values ​​of enumeration members are strings.

function changeDirection2(direction: Direction2) {
    
    
    console.log(direction);
}
enum Direction2 {
    
     Up = "UP", Down = "DOWN", Left = "LEFT", Right="RIGHT" };
changeDirection2(Direction2.Up);    //值为UP
changeDirection2(Direction2.Right); //值为RIGHT

String enumerations have no auto-increment behavior, so each member of a string enumeration must have an initial value.

Enums are one of the few features of TS that does not extend JavaScript at the type level (not just types).

Because other types are only treated as types, enumerations are not only used as types, but also provide values ​​​​(enumeration members all have values)

That is, other types are automatically removed when compiling to JS code. However, enum types are compiled to JS code!

//编译前
enum Direction2 {
    
     Up = "UP", Down = "DOWN", Left = "LEFT", Right="RIGHT" };

//编译后
var Direction2;
(function (Direction2) {
    
    
    Direction2["Up"] = "UP";
    Direction2["Down"] = "DOWN";
    Direction2["Left"] = "LEFT";
    Direction2["Right"] = "RIGHT";
})(Direction2 || (Direction2 = {
    
    }));

Explanation: Enumeration is similar to the function of the combination of literal type + joint type mentioned above, and is used to represent a clear set of optional value lists.

In general, it is recommended to use the combination of literal type + joint type, because this method is more intuitive, concise and efficient than enumeration.

2.13 any type

Any is not recommended!

This makes TypeScript become AnyScript(lose the advantage of TS type guards). Because when the type of the value is AnyYes, any operation can be performed on the transformation, and there will be no code prompt.

let obj: any = {
    
     x: 0 };

obj.bar = 100;
obj();
const s: number = obj;

Other implicitly typed anycases:

  1. Declare a variable without providing a type nor a default value
  2. function parameter without type

2.14 typeof

typeofAs we all know, operators are provided in JS to obtain the type of data in JS.

console.log(typeof "Hello IT飞牛");//打印:string

TS also provides typeofoperators: types that can refer to variables or properties in a type context (type queries).

Usage scenario: According to the value of an existing variable, obtain the type of the value to simplify type writing.

let p = {
    
     x: 1, y: 2 };
function formatPoint1(point: {
    
     x: number, y: number }) {
    
     }
function formatPoint2(point: typeof p) {
    
     }          //在类型注解上下文中使用typeof

// 以下两种写法等效
formatPoint1(p);
formatPoint2(p);

Three, TypeScript advanced type

There are many advanced types in TS, focus on learning advanced types:

  • class class
  • type compatibility
  • cross type
  • Generics and keyof
  • Index signature type and index query type
  • mapping type

1. class class

TypeScript fully supports the keywords introduced in ES2015class , and adds type annotations and other syntax (such as: visibility modifiers, etc.)

class Person {
    
    
    age: number
    gender: string

    constructor(age: number, gender: string) {
    
    
        this.age = age;
        this.gender = gender;
    }
}

let person = new Person(19, "hello");
  • Members are initialized, such as: age:numberAfter that, they can be this.ageaccessed through instance members.
  • A type annotation needs to be specified for the constructor, otherwise it is implicitly inferred any; the constructor does not need to return a value type.
class Point {
    
    
    x: number
    y: number

    scale(n: number): void {
    
    
        this.x *= n;
        this.y *= n;
    }
}

Explanation: Method type annotations (parameters and return values) are the same as function usage.

Two methods of class inheritance:

  1. extendsInherit parent class (available in ES6)
  2. implementsImplement the interface (provided in TS)

1.1 extends inherits the parent class

class Animal {
    
    
    move() {
    
    
        console.log("move along");
    }
}

class Dog extends Animal {
    
    
    bark() {
    
    
        console.log("汪");
    }
}
const dog = new Dog();
dog.move();
dog.bark();

//打印:
// move along
// 汪
  • extendsInheritance through keywords

  • If the subclass Doginherits the parent class Animal, then Dogthe instance object doghas all the properties and methods of the parent class Animaland the subclass at the same time.Dog

1.2 Simulating multiple inheritance

TypeScript does not support multiple inheritance, that is, it does not support class A extends B,C{...}such writing.
This is done because multiple integrations can potentially increase the complexity of the program.
For example B, Cthere are move()methods in , and Athe instance of , move()there will be ambiguity when calling the method, and you don't know which one to call;

There are two ways to implement multiple integration in ts:

  • mix
  • inherit class + implement interface

mix:

class B {
    
    
    breath(): string {
    
    
        return "i am breath";
    }
}

class C {
    
    
    fly(): string {
    
    
        return "i am fly";
    }
}

class A implements B, C {
    
    
    breath: () => string;
    fly: () => string;
}
//混合
function Mixins(targetClass: any, baseClass: any[]) {
    
    
    baseClass.forEach(item => {
    
    
        Object.getOwnPropertyNames(item.prototype).forEach(name => {
    
    
            if (name != "constructor") {
    
    
                targetClass.prototype[name] = item.prototype[name];
            }
        })
    });
}

Mixins(A, [B, C]);
let a = new A();
console.log(a.breath());
console.log(a.fly());

// 执行时,需要关闭tsconfig.json中强制初始化校验,否则程序执行失败。
//compilerOptions.strictPropertyInitialization=false

// 打印如下:
// i am breath
// i am fly

inherit class + implement interface

class Person1 {
    
    
    age: number
    gender: string

    constructor(age: number, gender: string) {
    
    
        this.age = age;
        this.gender = gender;
    }
}

interface Person2 {
    
    
    high: number;
    address: string;
}

class Person extends Person1 implements Person2 {
    
    
    high: number;
    address: string;

    constructor(age, gender, high, address) {
    
    
        super(age, gender);
        this.high = high;
        this.address = address;
    }
}

let person = new Person(29, "hello", 165, "上海市");
console.log(person)
//打印: Person { age: 29, gender: 'hello', high: 165, address: '上海市' }

1.3 implements realize the interface

interface Animal {
    
    
    move(): void
}

class Dog implements Animal {
    
    
    move() {
    
    
        console.log("move along");
    }
    bark() {
    
    
        console.log("汪");
    }
}
const dog = new Dog();
dog.move();
dog.bark();

//打印:
// move along
// 汪

Let the interface be implemented by implementskeywordclass

Person类Implementing an interface Singablemeans that all methods and properties specified Person类in must be available in .Singable接口

1.4 Class member visibility

Class member visibility: You can use TS to control whether classa method or property is classvisible to external code.

Visibility modifiers include:

  • public: shared
  • private: private
  • protected: protected

1.4.1 public

Indicates shared, public, shared members can be accessed anywhere, default visibility.

class Animal {
    
    
    public move(){
    
    
        console.log("move along");
    }
}
  1. Add keywords in front of class properties or methods publicto modify the property or method is common
  2. Because publicit is the default visibility, it can be omitted directly.

1.4.2 protected

Indicates that it is protected and is only visible in the class and subclasses (non-instance objects) where it is declared.

class Animal {
    
    
    protected move(){
    
    
        console.log("move along");
    }
}

class Dog extends Animal {
    
    
    bark() {
    
    
        this.move();
        console.log("汪");
    }
}
const dog = new Dog();
dog.bark();
// dog.move();  会报错

//打印:
// move along
// 汪
  1. Add keywords in front of class properties or methods protectedto modify properties or methods that are protected.
  2. thisThe protected members of the parent class can be accessed inside the methods of the subclass , but they are not visible to the instance.

1.4.3 private

Represents private, intended to be visible in the current class, and invisible to instance objects and subclasses.

class Animal {
    
    
    private move() {
    
    
        console.log("move along");
    }

    protected go() {
    
    
        this.move();
    }
}

class Dog extends Animal {
    
    
    bark() {
    
    
        // this.move(); 会报错
        this.go();
        console.log("汪");
    }
}
const dog = new Dog();
dog.bark();
// dog.move();  会报错
  • Add keywords in front of class properties or methods privateto modify the property or method as private.
  • Private properties or methods are only visible in the current class, and are invisible to subclasses and instance objects!

1.4.4 readonly

In addition to the visibility modifier, there is another common modifier: readonly(read-only modifier).

readonly: Indicates read-only, which is used to prevent the assignment of properties outside the constructor value.

class Person {
    
    
    readonly age: number = 18

    constructor(age: number) {
    
    
        this.age = age;
    }
}

let person = new Person(19);
console.log(person.age);
// person.age = 20;     报错
// console.log(person.age);
  • Using readonlykeywords to modify this attribute is read-only. Note that only attributes can be modified and methods cannot be modified.
  • Note: If agethe type annotation (for example, here number) after the attribute is not added, the agetype of the attribute is 18 (literal type).
  • Interfaces, or {}object types represented, can also be used readonly.

1.5 Use of get and set

class Company {
    
    
	// 记住 你要使用的名字的话 前面必须要加上_
	private _fullName: string;
	
	//get 的用法
	get fullName(): string{
    
               // 函数后(): string 这个的意思是 要求函数返回的类型必须是 string
		return this._fullName;
	}

    // set 的用法
    set fullName(newName: string) {
    
    
    	console.log("这里可以写一些你想要的操作的方法");
		this._fullName = newName;
	}
}

// 执行class 
const c = new Company();
c.fullName = "我是小白";

2. Type Compatibility

Two types of systems:

  1. Structural Type System (structural type system)
  2. Normal Type System (indicates the type system)

TS uses a structured type system, also known as duck typing (duck type), and type checking focuses on the shape that a value has.

2.1 Class Compatibility

That is, in a structural type system, two objects are considered to be of the same type if they have the same shape. The following variable is defined pas Pointa type, but its value is Point2Dan instance of , and there is no type error.

class Point {
    
    
    x: number
    y: number
}
class Point2D {
    
    
    x: number
    y: number
}
let p: Point = new Point2D();

Because TS is a structured type system, it only checks Pointwhether Point2Dthe structure of and is the same (the same, both have xand ytwo attributes, and the attribute types are also the same).

If it is in the Norinal Type System (for example, C#, Java, etc.), they are different classes and the types are not compatible.

Note: In a structured type system, it is not accurate to say that two objects are considered to be of the same type if they have the same shape.

A more prepared statement: For object types, ythe members of at least the xsame as and are xcompatible y(more members can be assigned to fewer).

The following assignments are possible:

class Point {
    
    
    x: number
    y: number
}
class Point2D {
    
    
    x: number
    y: number
}
class Point3D {
    
    
    x: number
    y: number
    z: number
}
let point1: Point = new Point2D();
let point2: Point = new Point3D();

In classaddition, other types in TS are also compatible with each other, including interface compatibility, function compatibility, etc.

2.2 Interface Compatibility

Compatibility between interfaces is similar to class, and classcan interfacealso be compatible with.

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

interface Point2D {
    
    
    x: number;
    y: number;
}
let p1: Point = {
    
     x: 1, y: 2 };
let p2: Point2D = p1;

interface Point3D {
    
    
    x: number;
    y: number;
    z: number;
}

let p3: Point3D = {
    
     x: 1, y: 2, z: 3 }
p2 = p3;

class Point3D {
    
    
    x: number;
    y: number;
    z: number;
}
let p4: Point2D = new Point3D();

2.3 Function Compatibility

The compatibility between functions is more complicated, and it needs to consider the number of parameters, parameter types, and return value types.

2.3.1 Number of parameters

type F1=(a:number)=>void
type F2=(a:number,b:number)=>void
let f1:F1
let f2:F2=f1;

const arr=["a","b","c"];
arr.forEach(()=>{
    
    })
arr.forEach((item)=>{
    
    })
  1. Less parameters can be assigned to more parameters, so f1 can be copied to f2
  2. forEachThe first parameter of the array method is a callback function, and the type in this example is:(value:string,index:number,array:string[])=>void
  3. It is actually very common to omit unused function parameters in JS, and this way of usage contributes to the compatibility between function types in TS.
  4. And because the callback function has a type, TS will automatically deduce item/index/arraythe type of the parameter

2.3.2 Parameter types

Parameter types at the same position must be the same (primitive type) or compatible (object type).

type F1=(a:number)=>void
type F2=(a:number,b:number)=>void
let f1:F1
let f2:F2=f1;

Function types F2are compatible with function types F1because the first argument of F1and is of the same type.F2

interface Point2D {
    
    
    x: number
    y: number
};
interface Point3D {
    
    
    x: number
    y: number
    z: number
};
type F2 = (p: Point2D) => void
type F3 = (p: Point3D) => void
let f2: F2
let f3: F3 = f2
f2 = f3
  1. This conflicts with the interface compatibility mentioned earlier
  2. Skill: Disassemble the object and regard each attribute as a parameter, then the one with less parameters f2can be assigned to the one with more parameters f3.

2.3.3 Return value type

If the return value is a primitive type, the two types must be the same at this time, such as F5andF6

type F5=()=>string
type F6=()=>string
let f5:F5
let f6:F6=f5;

If the return value type is an object type, the one with more members can be assigned to the one with fewer members, such as F7andF8

type F7 = () => {
    
     name: string }
type F8 = () => {
    
     name: string, age: number }
let f7: F7
let f8: F8
f7 = f8;

3. Crossover type

The cross-type function is similar to interface inheritance extends , which is used to combine multiple types into one type (often used for object types).

interface Person{
    
    name:string}
interface Contact{
    
    phone:string}
type PersonDetail=Person & Contact
//type PersonDetail={name:string;phone:string}; 等同于上方
let obj:PersonDetail={
    
    
    name:"IT飞牛",
    phone:"13312332123"
}

After using the intersection type, the new type PersonDetailhas all the attribute types of Personand at the same time.Contact

Comparison of cross-type and interface inheritance:

  1. The same point: both can realize the combination of object types
  2. The difference: when the two methods implement type combination, the methods for handling type conflicts between attributes with the same name are different.
interface A {
    
    
    fn: (val: number) => string
}
interface B extends A {
    
    
    // fn: (val: string) => string      出现同名方法或属性会报错
}
interface C {
    
    
    fn: (val: string) => string
}
type D = A & C;     //交叉类型中,同名属性或者方法不会报错

4. Generics

Generics allow functions to work with multiple types on the premise of ensuring type safety, so as to achieve reuse. It is often used in: functions, interfaces, and classes.

For example:

function id<Type>(value: Type): Type {
    
    
    return value;
}

console.log(id<number>(100));       //打印:100
// console.log(id(100)); 等效

console.log(id<string>("Hello"));   //打印:Hello
// console.log(id("Hello"));等效
  1. Syntax: Add (angle brackets) after the function name <>, and add type variables in angle brackets, such as here Type.
  2. A type variable Typeis a special type of variable that deals with types rather than values.
  3. The type variable is equivalent to a type container, which can capture the type provided by the user (the specific type is specified by the user when calling the function).
  4. Because Typeit is a type, it can be used as the type of function parameters and return values, indicating that the parameters and return values ​​have the same type.
  5. Type variable Type, which can be any legal variable name.

When the compiler cannot infer the type or the inferred type is inaccurate, it needs to explicitly pass in the type parameter, which cannot be omitted <>. like:id<number>(100)

4.1 Generic constraints

By default, the type variable of a generic function Typecan represent more than one type, which makes it impossible to access any properties. For example: id("a")Get the length of the parameter when calling the function.

function id<Type>(value:Type):Type{
    
    
    console.log(value.length);//报错,不能保证value上有length属性
    return value;
}

At this time, you need to use generic constraints to shrink the type.

4.1.1 Specifying more specific types

function id<Type>(value:Type[]):Type[]{
    
    
    console.log(value.length);
    return value;
}

Specify that the input is Type[]an array, as long as it is data, it must have lengthattribute values.

4.1.2 Add constraints

  1. Creates an interface describing constraints ILenghtthat requires lengthproperties.
  2. Using the interface via extendskeywords, adds constraints to generics (type variables).
  3. This constraint means: The type passed in must have lengthproperties. (For example, arrays have lengthattributes and can be passed in normally)
interface ILength {
    
     length: number }
function id<Type extends ILength>(value: Type): Type {
    
    
    console.log(value.length);
    return value;
}

Generic type variables can have multiple types, and type variables can also be constrained (for example, the second type variable is constrained by the first type variable).

For example: Create a function to get the value of a property in an object:

function getProp<MyType, MyKey extends keyof MyType>(obj: MyType, key: MyKey) {
    
    
    return obj[key];
}

let person = {
    
     name: "IT飞牛", age: 19 };
console.log(getProp(person, "name"));
  1. A second type variable is added MyKey, separated by a comma.
  2. keyofKeyword takes an object type, yielding a union type whose key names (which may be strings or numbers) are.
  3. In this example, keyof Typewhat is actually obtained is personall the union types built by the object, that is: 'name'|'age'.
  4. The type variable MyKeyis MyTypeconstrained, which can be understood as: MyKeyit can only be MyTypeany one of all keys, or it can only access the properties that exist in the object.

4.2 Generic interfaces

Interfaces can also be used with introspection to increase its flexibility and enhance its reusability.

interface IdFunc<MyType> {
    
    
    id: (value: MyType) => MyType
    ids: () => MyType[]
}

let obj: IdFunc<number> = {
    
    
    id(value) {
    
     return value; },
    ids() {
    
     return [1, 3, 5]; }
}

Remark:

  1. Add after the interface name <类型变量>, then this interface becomes a generic interface.
  2. The type variable of the interface is visible to all other members of the interface, that is, all members of the interface can use the type variable.
  3. When using a generic interface, it is necessary to specify a specific type (for example: here is IdFunc<number>)
  4. At this time, idthe parameter and return value types of the method are number; idsthe return value type of the method is number[];

In fact, the array in Js is a generic interface in TS. When we use different types of arrays, TS will automatically set the type variable to the corresponding type according to the different types of arrays.
insert image description here

4.3 Generic classes

class组件For example, the base class of React is a generic class, and Componentdifferent components have different sums .propsstate

4.4.1 Creating generic classes

  • Similar to a generic interface, by classadding it after the name <类型变量>, the class becomes a generic class.
  • The method here addadopts the type writing method in the form of arrow function.
class genericNumber<NumType>{
    
    
    defaultValue: NumType
    add: (x: NumType, y: NumType) => NumType
}

const myNum = new genericNumber<number>();
myNum.defaultValue = 10;

console.log(myNum.defaultValue);

ts-node greeter.tsWhen the above code is executed directly , there will be a prompt defaultValueand addno initialization, because tsconfig.jsoninitialization is required by default compilerOptions.strictPropertyInitialization=true. But doing so is not recommended.

There are three ways to circumvent:

  1. Use a non-empty assertion, add after the attribute :defaultValue!: NumType

  2. Use union types.defaultValue: NumType | null | undefined

  3. To use optional attributes, add " after the attribute ? :defaultValue?: NumType

4.4 Generic tool types

Some commonly used tool types are built into TS to simplify some common operations in TS.

Explanation: They are all implemented based on generics (generics are applicable to multiple types and are more general), and they are built-in and can be used directly in the code.

There are many types of these tools, mainly the following:

  1. Partial<Type>: Used to construct a type, and Typeset all properties of it to optional
interface Person {
    
    
    id: string
    children: number[]
}
type PartialProps = Partial<Person>;
  1. Readonly<Type>: Used to construct a type and Typeset all properties of it to read-only
interface Person {
    
    
    id: string
    children: number[]
}
type PartialProps = Readonly<Person>;
  1. Pick<Type,Keys>: Select a set of properties Typefrom which to construct a new type.
//Pick工具类型有两个类型变量:1、表示选择谁的属性 2、表示选择哪几个属性
//第二个类型变量,如果只选择一个则只传入该属性名即可。且只能是第一个类型变量中存在的属性
//构造出来的新类型PickProps,只有id和title两个属性类型。
interface Props {
    
    
    id: string
    title: string
    children: number[]
}
type PickProps = Pick<Props, "id" | "title">;
  1. Record<Keys,Type>: Constructs an object type with property key keysand property type Type.
//Record工具类型有两个类型变量:1、表示对象有哪些属性 2、表示对象属性的类型
//构建的新对象类型RecordObj表示:这个对象有三个属性类型分别为a/b/c,属性值的类型都是string[];
type RecordObj = Record<"a" | "b" | "c", string[]>;
let obj: RecordObj = {
    
     a: ["1"], b: ["2"], c: ["3"] };
//下面指定一个键类型是number,值类型时string的对象
let arg4: Record<number, string> = {
    
    
    1: "a",
    2: "b",
    3: "c",
};

5. Index signature type

Arrays are a special type of object in JS, especially in that the keys (indexes) of the array are numeric types.

Also, an array can have any number of elements. Therefore, the index signature type is also used in the generic interface corresponding to the array.

interface MyArray<T> {
    
    
    [n: number]: T
}
let arr: MyArray<number> = [1, 3, 5];

Remark:

  1. MyArrayThe interface mimics the native array interface and uses [n:number]as the index signature type.
  2. The index signature type indicates that any numbertype of key (index) can appear in the array, or there can be any number of elements in the array.
  3. It also meets numberthe premise that the array index is a type.
  4. Tis generic.

6. Mapping type

Mapping types: create new types (object types) based on old types, reduce repeated development, and improve development efficiency.

For example: a type PropKeyshas x/y/z in x/y/zanother type Type1, and the types Type1in x/y/zare the same.

type PropKeys = "x" | "y" | "z";
// 传统写法
type Type1 = {
    
     x: number, y: number, z: number };
// 映射类型
type Type2 = {
    
     [key in PropKeys]: number };
  1. The mapping type is based on the index signature type, so the syntax is similar to the index signature type, also using[]
  2. Key in PropKeysIndicates that any one of the s union types Keycan be made, similar toPropKeyforin(lei k in obj)
  3. Type2New object types and Type1structures created using mapped types are exactly the same
  4. Note: Mapped types can only be used in type aliases, not interfaces
type Props = {
    
     a: number; b: number; c: number };
// 映射类型:根据对象类型来创建
type Type3 = {
    
     [key in keyof Props]: number };

Remark:

  1. First, execute the union type keys Propsthat gets Propsall the keys in the object type:“a”|"b"|"c"
  2. Then, Key in ...it means Keythat it can be Propsany one of all the key names in .

In fact, the generic tool types mentioned above (for example: Partial<Type>) are all implemented based on the mapping type

For example, Partial<Type>the implementation of:

type Partial<T> = {
    
    
    [P in keyof T]: T[P]
}
type Props = {
    
     a: number; b: number; c: boolean }
type PartialProps = Partial<Props>

4. Type declaration file

Type declaration files are used to provide type information for existing JS libraries. In this way, when using these libraries in TS projects, just like using TS, there will be mechanisms such as code hints and type protection.

1. Two file types in TS

1.1 .tsDocumentation

  1. Contains both type information and executable code
  2. Can be compiled into a .js file and then execute the code

1.2 .d.tsDocumentation

  1. A type declaration file that contains only type information
  2. No .js files will be generated, only used to provide type information

.tsIt is a code implementation file; .d.tsit is a type declaration file;

If you want to provide type information to a JS library, use .d.tsa file.

2. Instructions for use of the type declaration file

2.1 Using an existing type declaration file

2.1.1 Built-in type declaration files

TS provides declaration files for all standardized built-in APIs available to the JS runtime.

For example, when using an array, all methods of the array will have corresponding code hints and type information:
insert image description here
in fact, these are built-in type declaration files provided by TS. You can view the content of built-in type files by pressing Ctrl+left mouse button .

2.1.2 The type declaration file of the third-party library

Almost all commonly used third-party libraries have corresponding type declaration files. There are two forms:

  1. The library comes with type declaration files, such asaxios
    insert image description here
  2. DefinitelyTypedprovided by

DefinitelyTypedis a github repository for high-quality TypeScript type declarations.

You can npm/yarndownload the TS type declaration packages provided by this repository through . The format of these packages is: @types/ *. For example: @types/react, @types/lodashetc., when @types/*the type declaration package is installed, TS will also automatically load the type life package to provide the type declaration of the modified library.

In actual project development, if the third-party library you use does not have its own declaration file, VSCode will give a clear prompt.

insert image description here

The TypeScript official website provides @types/*the query address of the library: Type Search

2.2 Create your own type declaration file

2.2.1 Shared types within a project

If .tsthe same type is used in multiple files, you can create .d.tsa file to provide the type at this time to achieve type sharing.

Steps:

  1. Create index.d.tsa type declaration file
  2. Create types that need to be shared and use export to export (types in TS can also use import/export to achieve modular functions)
  3. In the file that needs to use the shared type .ts, importit can be imported ( .d.tswhen the suffix is ​​imported, it is directly omitted).

2.2.2 Provide type declarations for existing JS files

  1. When migrating JS projects to TS projects, in order to allow existing .jsfiles to have type declarations.
  2. Become a library author and create libraries for others to use.

Files are also available in TS projects .js. When importing .jsa file, TS will automatically load the file with .jsthe same name .d.ts, and the type declaration has been provided.

declareKeyword: Used for type declarations, .jsdeclaring types for variables that already exist in other places (such as files) instead of creating a new variable.

  1. For type, interfaceetc., these are clearly of the TS type, and declarekeywords can be omitted.
  2. For let, functionand so on have double meanings (js and ts are both available), declarekeywords should be used to clearly specify that this is used for type declaration.

Guess you like

Origin blog.csdn.net/bobo789456123/article/details/126925635