[TS] TypeScript Basic Learning Guide

TypeScript basics

a data type

Basic types: Boolean, Number, String, null, undefinedand ES6's Symbol and ES10's BigInt .

1 basic data type

1.1 string

String types are stringdefined .

//普通声明
let a: string = '123'
 
//也可以使用es6的字符串模板
let str: string = `dddd${
      
      a}`

1.2 number

Use numberto define number type, support hexadecimal, decimal, octal, binary.

let notANumber: number = NaN;	//Nan
let num: number = 123;			//普通数字
let infinityNumber: number = Infinity;//无穷大
let decimal: number = 6;		//十进制
let hex: number = 0xf00d;		//十六进制
let binary: number = 0b1010;	//二进制
let octal: number = 0o744;		//八进制

1.3 boolean

// 这样会报错 应为事实上 new Boolean() 返回的是一个 Boolean 对象 
let createdBoolean: boolean = new Boolean(1)

//  
let createdBoolean: Boolean = new Boolean(1)
let booleand: boolean = true //可以直接使用布尔值
let booleand2: boolean = Boolean(1) //也可以通过函数返回布尔值

1.4 object

const user: {
    
     _id: number, name: string } = {
    
     id: 1, name: 'Li'};

// ? 表示可选属性
const user: {
    
     _id: number, name?: string } = {
    
     id: 1, name: 'Li'};

// [propName: string]: any 任意可选属性
const user: {
    
     _id: number, name: string, [propName: string]: any } 
= {
    
     id: 1, name: 'Li', age: 18, gender: '男'};

//特殊的 可以赋值一个函数,也不会报错
let a: object;
a:{
    
    };
a = function () {
    
    };

1.5 void

JavaScript has no concept of null values.

void can be used in TypeScript 表示没有任何返回值的函数.

function voidFn():void {
    
    
    // ...
}

void 类型的用法: 主要是用在不希望调用者关心函数返回值的情况下,比如通常的异步回调函数。

void can define undefined and null

let u: void = undefined;
let n: void = null;

1.6 unll and undefined

let u: undefined = undefined; // 定义 undefined
let n: null = null; // 定义 null

The difference between void and (undefined, null):

Undefined and null are all types 子类, that is, variables of undefined and null types can be assigned to variables of other types.

//这样是没问题的
let test: null = null
let s: string = "1"
 
s = test // 可以
 
//或者这样的
let test: undefined = undefined
let s: string = "1"
 
s = test // 可以

But variables of void type cannot be assigned to other types.

// void 类型的变量不能赋值给其他类型,下面这样写会报错
let test: void = undefined;
let s: string = '1';

s = test; // 报错

2 any type

any type and unknown top-level type.

2.1 any

Any means that we define a variable of any type, which does not require type checking and can be switched arbitrarily.

let anys:any = 123
anys = '123'
anys = true

// 声明变量的时候没有指定任意类型,默认为 any
let anys;
anys = '123'
anys = true

**Disadvantages:** If you use any, you will lose the role of TS type detection

2.2 unknown

unknown means unknown . The unknown type introduced in TypeScript 3.0 is also considered a top type, but it is safer. As with any, 所有类型都可以分配给 unknown.

The unknown type is more strict than any. When you want to use any, you can try to use unknown

//unknown 可以定义任何类型的值
let value: unknown;

value = true;             // OK
value = 42;               // OK
value = "Hello World";    // OK
value = [];               // OK
value = {
    
    };               // OK
value = null;             // OK
value = undefined;        // OK
value = Symbol("type");   // OK

//这样写会报错unknow类型不能作为子类型只能作为父类型 any可以作为父类型和子类型
//unknown类型不能赋值给其他类型
let names:unknown = '123'
let names2:string = names

//这样就没问题 any类型是可以的
let names:any = '123'
let names2:string = names   

//unknown可赋值对象只有unknown 和 any
let bbb:unknown = '123'
let aaa:any= '456'

aaa = bbb

difference 2

// 如果是 any 类型在对象没有这个属性的时候还在获取是不会报错的
let obj:any = {
    
    b:1}
obj.a

// 如果是 unknown 是不能调用属性和方法
let obj:unknown = {
    
    b:1,ccc:():number=>213}
obj.b
obj.ccc()

2.3 Differences

1 unknown is stricter and safer than any.

unknown 只能作为父类,也就是可以将任意类型是变量赋值给 unknown 类型的变量,但是不能将 unknown 类型的变量赋值给其他任意类型。

any 既可以作父类又可以作子类,即可以将 any 类型的变量赋值给其他任意类型,也可以将任意类型的变量赋值给 any类型。

2 for properties and methods

unknown 类型是不能调用属性和方法的。

any 类型是可以的,而且在对象上获取一个不存在的属性也是不会报错的。

3 interface interface

3.1 what is the interface

In TypeScript, the way to define objects is to use keywords interface(interfaces).

It can be understood as using interface to define a constraint, so that the structure of the data meets the format of the constraint.

That is to say, a special data type of multiple structures can be defined through the interface.

// 使用 interface 定义一个 Person 的类型,其中有两个为 string 的属性
interface Person {
    
    
    b:string,
    a:string
}

// 这里使用上面定义的 Person 接口约束
// 目前这样写是会报错的,因为使用接口约束的时候,属性的数量必须与接口定义的属性保持一致。不能多不能少
const person:Person  = {
    
    
    a:"213"
    // 属性的数量要与接口定义的属性保持一致
    // 但可以使用可选属性来解决这个问题 ?.
}

3.2 Interfaces with the same name will be merged

interface A {
    
     name:string }
interface A {
    
     age:number }

let x:A = {
    
     name:'xx', age:20 }

3.3 interface can use extends for inheritance

// 定义一个 A 接口
interface A {
    
    
    name:string
}

// B 接口继承 A 中定义的属性
interface B extends A{
    
    
    age:number
}

// 使用 B 接口
let test:B = {
    
    
    age:18,
    name:"string"
}

3.4 Optional attributes using the ? operator

//可选属性的含义是该属性可以不存在,所以说这样写也是没问题的
interface Person {
    
    
    a:string,
    b?:string
}

const person:Person  = {
    
    
    a:"213"
}

3.5 Arbitrary property [propName: string]

It should be noted that once any attribute is defined, the type of both definite and optional attributes must be a subset of its type:

// 使用 [propName: string]: any 定义任意属性
interface Person {
    
    
    b?:string,
    a:string,
    [propName: string]: any;
}

//在这个例子当中我们看到接口中并没有定义C但是并没有报错
//应为我们定义了[propName: string]: any;
//允许添加新的任意属性
const person:Person  = {
    
    
    a:"213",
    c:"123"
}

3.6 Read-only attribute readonly

readonly 只读属性是不允许被赋值的只能读取

//这样写是会报错的
//应为a是只读的不允许重新赋值
interface Person {
    
    
    b?: string,
    readonly a: string,
    [propName: string]: any;
}

const person: Person = {
    
    
    a: "213",
    c: "123"
}

person.a = 123

3.7 Add function

interface Person {
    
    
    b?: string,
    readonly a: string,
	[propName: string]: any,
	cb:()=>void
}

const person: Person = {
    
    
    a: "213",
    c: "123",
    cb:()=>{
    
    
        console.log(123)
    }
}

3.8 Summary:

1 使用 interface 定一个特殊的可约束的数据类型结构
2 在使用接口约束的时候,变量的属性与接口约束的属性保持一致,不能多也不能少。
3 重名的 interface 会进行合并
4 interface 可以使用 extends 进行继承
5 可以使用 ? 操作符定义可选属性,解决第二点的问题
6 [propName: string]: any 定义任意属性
7 readonly 只读属性,不允许被赋值,只能读取
8 ()=> void 同时是可以添加函数的。

4 array types

4.1 Basic types

Array types are defined using type brackets : number[]

// 定义数组类型时需要指定类型,如数字类型、字符串类型等,以及任意类型
let arr: number[] = [1, 2, 3]; 
let arr2: string[] = ["1", "2"];
let arr3: any[] = [1, "2", true]; // 数组中可以存任意类型的元素

// 如果指定的特定的类型,是不允许数组中有除此之外类型的元素的
let arr: number[] = [1,2,3,'1']; // 此时会报错
arr.unshift('1'); //操作方法添加也是不允许的

4.2 Array Generics

Rule Array <type>

let arr: Array<number> = [1, 2, 3];

4.3 Representing arrays with interfaces

Generally used to describe class arrays.

interface NumberArray {
    
    
    [index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
//表示:只要索引的类型是数字时,那么值的类型必须是数字。

4.5 Multidimensional arrays

let data:number[][] = [[1,2], [3,4]];

4.6 arguments class array

function Arr(...args:any): void {
    
    
    console.log(arguments)
    //错误的arguments 是类数组不能这样定义
    let arr:number[] = arguments
}
Arr(111, 222, 333)
 
 
function Arr(...args:any): void {
    
    
    console.log(arguments) 
    //ts内置对象IArguments 定义
    let arr:IArguments = arguments
}
Arr(111, 222, 333)
 
//其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是:
interface IArguments {
    
    
    [index: number]: any;
    length: number;
    callee: Function;
}

5 Function types

5.1 Define function type

// 定义一个函数
const fn = (name: string, age:number): string => {
    
    
    return name + age
}

// 注意,参数不能多传,也不能少传 必须按照约定的类型来
fn('张三',18) 

5.2 Optional arguments to functions ?

//通过 ? 表示该参数为可选参数
const fn = (name: string, age?:number): string => {
    
    
    return name + age
}
fn('张三')

5.3 Default values ​​for function parameters

const fn = (name: string = "我是默认值"): string => {
    
    
    return name
}
fn()

5.4 Interface definition function

//定义参数 num 和 num2  :后面定义返回值的类型
interface Add {
    
    
    (num:  number, num2: number): number
}
 
const fn: Add = (num: number, num2: number): number => {
    
    
    return num + num2
}
fn(5, 5)
 
 
interface User{
    
    
    name: string;
    age: number;
}
function getUserInfo(user: User): User {
    
    
  return user
}

5.5 Define remaining parameters...

const fn = (array:number[], ...items:any[]):any[] => {
    
    
       console.log(array,items)
       return items
}

let a:number[] = [1,2,3]

fn(a,'4','5','6')

5.6 Function overloading

Overloading means that the method name is the same, but the parameters are different, and the return type can be the same or different.

If the parameter types are different, the parameter type should be set to any.

Different number of parameters You can make different parameters optional.

function fn(params: number): void

function fn(params: string, params2: number): void

function fn(params: any, params2?: any): void {
    
    
    console.log(params)
	console.log(params2)
}


fn(123)

fn('123',456)

6 Type Assertion|Union Type|Intersection Type

6.1 Union types |

// 例如某些身份证号会有 X 存在,即会有数字又有字母,因此可以使用联合类型
// 使用一个竖线 | 来定义联合类型的变量
let ID: number | string = '412722xxx';

Functions use union types

const fn = (something:number | boolean):boolean => {
    
    
     return !!something
}

6.2 Intersection type&

A collection of multiple types, the union object will have all members of all union types

interface People {
    
    
  age: number,
  height: number
}
interface Man{
    
    
  sex: string
}

// 使用 & 联合类型
const user = (man: People & Man) => {
    
    
  console.log(man.age)
  console.log(man.height)
  console.log(man.sex)
}

user({
    
    age: 18,height: 180,sex: 'male'});

6.3 Type Assertions

Use type assertion to infer which interface the incoming value belongs to.

Syntax: 值 as 类型or<类型>值

interface A {
    
    
       run: string
}
 
interface B {
    
    
       build: string
}
 
const fn = (type: A | B): string => {
    
    
       return type.run
}
//这样写是有警告的应为B的接口上面是没有定义run这个属性的
interface A {
    
    
       run: string
}
 
interface B {
    
    
       build: string
}
 
const fn = (type: A | B): string => {
    
    
       return (type as A).run
}
//可以使用类型断言来推断他传入的是A接口的值

Note: type assertion can only "cheat" the TS compiler, if there is an error, it cannot be avoided.

Conversely, misuse of type assertions can lead to runtime errors.

Use any ad-hoc assertion

//这样写会报错因为window没有abc这个东西
window.abc = 123; 
      
//可以使用any临时断言在 any 类型的变量上,访问任何属性都是允许的
(window as any).abc = 123; 

as const
is an assertion of literal values, which is different from const directly defining constants

in the case of普通类型跟直接const 声明是一样的

const names = '小满'
names = 'aa' //无法修改


let names2 = '小满' as const
names2 = 'aa' //无法修改


// 数组
let a1 = [10, 20] as const;
const a2 = [10, 20];

a1.unshift(30); // 错误,此时已经断言字面量为[10, 20],数据无法做任何修改
a2.unshift(30); // 通过,没有修改指针

Type assertions are irrelevant

In the following example, asserting that something is boolean can be compiled, but it is useless and will not affect the result, because the type assertion will be deleted during compilation

function toBoolean(something: any): boolean {
    
    
    return something as boolean;
}

toBoolean(1);
// 返回值为 1

7 built-in objects

There are many built-in objects in JavaScript that can be directly used as defined types in TypeScript.

7.1 ECMAScript built-in objects

Boolean、Number、String、RegExp、Date、Error

let b: Boolean = new Boolean(1)
console.log(b)

let n: Number = new Number(true)
console.log(n)

let s: String = new String('字符串String')
console.log(s)

let d: Date = new Date()
console.log(d)

let r: RegExp = /^1/
console.log(r)

let e: Error = new Error("error!")
console.log(e)

7.2 DOM and BOM built-in objects

Document、HTMLElement、Event、NodeList 等

let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
//读取div 这种需要类型断言 或者加个判断应为读不到返回null
let div:HTMLElement = document.querySelector('div') as HTMLDivElement
document.addEventListener('click', function (e: MouseEvent) {
    
    
    
});

//dom元素的映射表
interface HTMLElementTagNameMap {
    
    
    "a": HTMLAnchorElement;
    "abbr": HTMLElement;
    "address": HTMLElement;
    "applet": HTMLAppletElement;
    "area": HTMLAreaElement;
    "article": HTMLElement;
    "aside": HTMLElement;
    "audio": HTMLAudioElement;
    "b": HTMLElement;
    "base": HTMLBaseElement;
    "bdi": HTMLElement;
    "bdo": HTMLElement;
    "blockquote": HTMLQuoteElement;
    "body": HTMLBodyElement;
    "br": HTMLBRElement;
    "button": HTMLButtonElement;
    "canvas": HTMLCanvasElement;
    "caption": HTMLTableCaptionElement;
    "cite": HTMLElement;
    "code": HTMLElement;
    "col": HTMLTableColElement;
    "colgroup": HTMLTableColElement;
    "data": HTMLDataElement;
    "datalist": HTMLDataListElement;
    "dd": HTMLElement;
    "del": HTMLModElement;
    "details": HTMLDetailsElement;
    "dfn": HTMLElement;
    "dialog": HTMLDialogElement;
    "dir": HTMLDirectoryElement;
    "div": HTMLDivElement;
    "dl": HTMLDListElement;
    "dt": HTMLElement;
    "em": HTMLElement;
    "embed": HTMLEmbedElement;
    "fieldset": HTMLFieldSetElement;
    "figcaption": HTMLElement;
    "figure": HTMLElement;
    "font": HTMLFontElement;
    "footer": HTMLElement;
    "form": HTMLFormElement;
    "frame": HTMLFrameElement;
    "frameset": HTMLFrameSetElement;
    "h1": HTMLHeadingElement;
    "h2": HTMLHeadingElement;
    "h3": HTMLHeadingElement;
    "h4": HTMLHeadingElement;
    "h5": HTMLHeadingElement;
    "h6": HTMLHeadingElement;
    "head": HTMLHeadElement;
    "header": HTMLElement;
    "hgroup": HTMLElement;
    "hr": HTMLHRElement;
    "html": HTMLHtmlElement;
    "i": HTMLElement;
    "iframe": HTMLIFrameElement;
    "img": HTMLImageElement;
    "input": HTMLInputElement;
    "ins": HTMLModElement;
    "kbd": HTMLElement;
    "label": HTMLLabelElement;
    "legend": HTMLLegendElement;
    "li": HTMLLIElement;
    "link": HTMLLinkElement;
    "main": HTMLElement;
    "map": HTMLMapElement;
    "mark": HTMLElement;
    "marquee": HTMLMarqueeElement;
    "menu": HTMLMenuElement;
    "meta": HTMLMetaElement;
    "meter": HTMLMeterElement;
    "nav": HTMLElement;
    "noscript": HTMLElement;
    "object": HTMLObjectElement;
    "ol": HTMLOListElement;
    "optgroup": HTMLOptGroupElement;
    "option": HTMLOptionElement;
    "output": HTMLOutputElement;
    "p": HTMLParagraphElement;
    "param": HTMLParamElement;
    "picture": HTMLPictureElement;
    "pre": HTMLPreElement;
    "progress": HTMLProgressElement;
    "q": HTMLQuoteElement;
    "rp": HTMLElement;
    "rt": HTMLElement;
    "ruby": HTMLElement;
    "s": HTMLElement;
    "samp": HTMLElement;
    "script": HTMLScriptElement;
    "section": HTMLElement;
    "select": HTMLSelectElement;
    "slot": HTMLSlotElement;
    "small": HTMLElement;
    "source": HTMLSourceElement;
    "span": HTMLSpanElement;
    "strong": HTMLElement;
    "style": HTMLStyleElement;
    "sub": HTMLElement;
    "summary": HTMLElement;
    "sup": HTMLElement;
    "table": HTMLTableElement;
    "tbody": HTMLTableSectionElement;
    "td": HTMLTableDataCellElement;
    "template": HTMLTemplateElement;
    "textarea": HTMLTextAreaElement;
    "tfoot": HTMLTableSectionElement;
    "th": HTMLTableHeaderCellElement;
    "thead": HTMLTableSectionElement;
    "time": HTMLTimeElement;
    "title": HTMLTitleElement;
    "tr": HTMLTableRowElement;
    "track": HTMLTrackElement;
    "u": HTMLElement;
    "ul": HTMLUListElement;
    "var": HTMLElement;
    "video": HTMLVideoElement;
    "wbr": HTMLElement;
}

8 classes

ES6 provides a way of writing closer to traditional languages, introducing the concept of classclass as an object template. Classes can be defined with the class keyword.

Basically, the ES6 class can be regarded as just a syntactic sugar, and most of its functions can be achieved by ES5.

The new class writing method just makes the object prototype writing method clearer and more like the syntax of object-oriented programming.

8.1 class definition variable

//定义类
class Person {
    
    
    name: string
    age: number = 0
    constructor (name: string,age: number) {
    
    
 		this.name = name
        this.age = age
    }
    run () {
    
    
        
    }
}

/* 注意:
	1 不允许在 constructor 定义变量,需要在 constructor 上面先声明
	2 如果定义了变量不使用,也会报错的,通常给一个默认值或者进行赋值
*/

8.2 Class modifiers

There are three:

​ public: if not written, the default is public, public, accessible both internally and externally

​ private: private attribute, only internal access

​ protected:

class Person {
    
    
    public name: string
    private age: number
    protected some: any
    constructor (name: string, ages: number, some: any){
    
    
        this.name = name
        this.age = ages
        this.some = some
    }
}

let zs = new Person('张三', 19, 1);
zs.name 	// 可以访问
zs.age 		// 私有属性,只能在内部访问,不能在外部访问
zs.some		// protected 定义的变量也是私有的,只能在内部和继承的子类中访问,不能在外部访问
class Person {
    
    
    public name:string
    private age:number 
    protected some:any
    constructor (name:string,ages:number,some:any) {
    
    
       this.name = name
       this.age = ages
       this.some = some
    }
    run () {
    
    
 
    }
}
 
class Man extends Person{
    
    
    constructor () {
    
    
        super("张三",18,1)
        console.log(this.some)
    }
    create () {
    
    
       console.log(this.some)
    }
}
let xiaoman = new Person('小满',18,1)
let man = new Man()
man.some

8.3 static static properties and static methods

class Person {
    
    
    static nb: string
	static aaa () {
    
    
        return console.log(123467)
	}
    constructor () {
    
    
        this.nb // 此时会报错的
        this.aaa() // 也会报错
    }
}
Person.nb 	// 可以
Person.aaa()// 可以

// static 静态属性,不能通过 this 去访问,只能通过类名去调用
// static 静态方法,不能通过 this 去调用,也是通过类名去调用

Note: If both functions are static , they can call each other through this .

class Person {
    
    
    static nb: string
	static aaa () {
    
    
        return console.log(123467)
	}
	static bbb () {
    
    
        return this.aaa()
	}
    constructor () {
    
    
    }
}

8.4 interface definition class

 
interface PersonClass {
    
    
    get(type: boolean): boolean
}
 
interface PersonClass2{
    
    
    set():void,
    asd:string
}
 
class A {
    
    
    name: string
    constructor() {
    
    
        this.name = "123"
    }
}
 
// 使用关键字 implements,后面跟 interface 的名字,多个用都好隔开
// 继承还是用 extends
class Person extends A implements PersonClass,PersonClass2 {
    
    
    asd: string
    constructor() {
    
    
        super()
        this.asd = '123'
    }
    get(type:boolean) {
    
    
        return type
    }
    set () {
    
    
    }
}

8.5 Abstract classes

Application scenario If the class you wrote is useless after instantiation, I can define it as an abstract class

Or you can also use it as a base class -> implement some methods of the base class by inheriting a derived class

Let's look at an example. The following code will report an error that the abstract class cannot be instantiated.

abstract class A {
    
    
   public name:string

}

new A()

Example 2

We defined the getName abstract method in class A but for implementation

Our class B implements the abstract method defined by A. If it is not implemented, no error will be reported.

The abstract method we define must be implemented in the derived class

abstract class A {
    
    
   name: string
   constructor(name: string) {
    
    
      this.name = name;
   }
   print(): string {
    
    
      return this.name
   }

   abstract getName(): string
}

class B extends A {
    
    
   constructor() {
    
    
      super('小满')
   }
   getName(): string {
    
    
      return this.name
   }
}

let b = new B();

console.log(b.getName());

video case

//1. class 的基本用法 继承 和 类型约束
//2. class 的修饰符 readonly  private protected public
//3. super 原理
//4. 静态方法
//5. get set
interface Options {
    
    
    el: string | HTMLElement
}

interface VueCls {
    
    
    init(): void
    options: Options
}

interface Vnode {
    
    
    tag: string
    text?: string
    props?: {
    
    
        id?: number | string
        key?: number | string | object
    }
    children?: Vnode[]
}

class Dom {
    
    
    constructor() {
    
    ```
}
 
private createElement(el: string): HTMLElement {
    return document.createElement(el)
}
 
protected setText(el: Element, text: string | null) {
    el.textContent = text;
}
 
protected render(createElement: Vnode): HTMLElement {
    const el = this.createElement(createElement.tag)
    if (createElement.children && Array.isArray(createElement.children)) {
        createElement.children.forEach(item => {
            const child = this.render(item)
            this.setText(child, item.text ?? null)
            el.appendChild(child)
        })
    } else {
        this.setText(el, createElement.text ?? null)
    }
    return el;
}
​```

}

 

class Vue extends Dom implements VueCls {
    
    
    options: Options
    constructor(options: Options) {
    
    
        super()
        this.options = options;
        this.init()
    }

   static version () {
    
    
      return '1.0.0'
   }

   public init() {
    
    
        let app = typeof this.options.el == 'string' ? document.querySelector(this.options.el) : this.options.el;
        let data: Vnode = {
    
    
            tag: "div",
            props: {
    
    
                id: 1,
                key: 1
            },
            children: [
                {
    
    
                    tag: "div",
                    text: "子集1",
                },
                {
    
    
                    tag: "div",
                    text: "子集2"
                }
            ]
        }
        app?.appendChild(this.render(data))
        console.log(app);```
    this.mount(app as Element)
}
​```

   public mount(app: Element) {
    
    
        document.body.append(app)
    }
}

const v = new Vue({
    
    
    el: "#app"
})


9-tuple

9.1 Use of tuples

Concept: A Tuple is a collection of a fixed number of elements of different types.

If you need a fixed-size collection of values ​​of different types, you can use tuples.

let arr:[number,string] = [1,'string']
 
 
let arr2: readonly [number,boolean,string,undefined] = [1,true,'sring',undefined]

// 元组和集合的不同之处在于:
// 元组中的元素类型可以是不同的,而且数量是固定的。
// 元组的好处在于可以把多个元素作为一个单元传递。
// 如果一个方法需要返回多个值,可以把这多个值作为元组返回,而不需要创建额外的类来表示。

When assigning or accessing an element with a known index, the correct type is obtained:

let arr:[number,string] = [1,'string']
arr[0].length //error
arr[1].length //success
 
//数字是没有length 的

Tuple types can also support custom names and become optional

let a: [x: number, y?: boolean] = [1]

9.2 Out-of-bounds elements

let arr:[number,string] = [1,'string']
 
arr.push(true)//error

For out-of-bounds elements, its type is restricted to the union type (that is, the type you define in the tuple)

let arr: [number, string] = [1, 'string'];

arr.push(false); // 会提示 类型Boolean的参数不能赋值给类型 string|number 的参数

Application scenarios, such as defining the data returned by Excel

let excel: [string, string, number, string][] = [
    ['title', 'name', 1, '123'],
    ['title', 'name', 1, '123'],
    ['title', 'name', 1, '123'],
    ['title', 'name', 1, '123'],
    ['title', 'name', 1, '123'],
]

10 enumeration enum

There is no concept of enums in JS. TS can define enumerations using enumthe keyword .

10.1 Numeric enumerations

// ts 定义的枚举中每一个组员默认都是从0开始的
enum Types {
    
    
	Red,
    Green,
    Blue
}
// 等同于
enum Types {
    
    
	Red = 0,
    Green = 1,
    Blue = 2
}

growth type

The defined number enumeration can specify the initial value, and the rest of the group members will automatically grow.

// 例如 Red 初始化为 1,其余成员会从 1 开始自动增长。
// 即 Types 的 Green 为 2,Blue 为 3
enum Types{
    
    
   Red = 1,
   Green,
   BLue
}

10.2 String Enumerations

Inside a string enum, each member must be initialized with a string literal, or with another string enum member.

enum Types{
    
    
   Red = 'red',
   Green = 'green',
   BLue = 'blue'
}
// 由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。

10.3 Heterogeneous enums

A heterogeneous enumeration is one that can mix string and number members.

enum Types{
    
    
   No = "No",
   Yes = 1,
}

10.4 Interface enumeration

// 定义一个枚举 Types
enum Types {
    
    
	yyds,
	dddd
}

// 定义一个接口 A,有个属性 red 值为 Types.yyds
interface A {
    
    
	red:Types.yyds
}

// 声明对象的时候要遵循这个原则
let obj:A = {
    
    
	red: Types.yyds
}

10.5 const enums

The enumeration declared by const will be compiled into a constant, and the enumeration of ordinary declaration will be compiled into an object.

Both let and var are not allowed to declare enums.

const enum Types{
    
    
   No = "No",
   Yes = 1,
}
/*
	大多数情况下,枚举是十分有效的方案。 然而在某些情况下需求很严格。 为了避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问,我们可以使用 const枚举。 常量枚举通过在枚举上使用 const修饰符来定义
*/

10.6 Reverse mapping

It contains forward mapping (name -> value) and reverse mapping (value -> name)

Note: No reverse mapping will be generated for string enum members.

// 定义一个枚举 Enum
enum Enum {
    
    
   fall
}

let a = Enum.fall;
console.log(a); //0

let nameOfA = Enum[a]; 
console.log(nameOfA); //fall

11 Type inference | Type aliases

11.1 Type inference

1 A variable is declared, but the type is not defined.

TS will guess when the type is not explicitly specified, and give a guessed type, which is type inference.

// 此时 TS 会推论变量 str 是 string 类型
let str = 'LXTL'; 

// 因此不能够再赋值给别的类型。 此时会报错,不能将 number 分配给 string
str = 123456; 

2 A variable is declared without a type and without assignment.

At this time, TS will be inferred as any type, and any operation can be performed.

let xiaoxu

xiaoxu = 18
xiaoxu = true
xiaoxu = 'xiaoxu'

11.2 Type alias type

type The key is to define a name for a type, which is mostly used to conform to the type.

1 Define type aliases

// 定义一个 string 的类型为 str
type str = string

// 使用 str 类型
let s: str = 'LXBM'

2 Define function aliases

type str = () => string
 
let s: str = () => "我是小满"

3 Define union type aliases

type str = string | number
 
let s: str = 123
 
let s2: str = '123'

4 Define an alias for the value

type value = boolean | 0 | '213'
 
let s:value = true //变量s的值  只能是上面value定义的值

11.3 The difference between type and interface

1.interface可以继承  type 只能通过 & 交叉类型合并

2.type 可以定义 联合类型 和 可以使用一些操作符 interface不行

3.interface 遇到重名的会合并 type 不行

11.4 advanced usage of type

The value on the left will be a subtype of the value on the right, following the inclusion relationship of the upper and lower in the graph

type a = 1 extends number ? 1 : 0 //1
 
type a = 1 extends Number ? 1 : 0 //1
 
type a = 1 extends Object ? 1 : 0 //1
 
type a = 1 extends any ? 1 : 0 //1
 
type a = 1 extends unknow ? 1 : 0 //1
 
type a = 1 extends never ? 1 : 0 //0

12 never

12.1 what is never

neverTypes are used in TS to represent state that should not exist.

// 返回never的函数必须存在无法达到的终点
 
// 因为必定抛出异常,所以 error 将不会有返回值
function error(message: string): never {
    
    
    throw new Error(message);
}
 
// 因为存在死循环,所以 loop 将不会有返回值
function loop(): never {
    
    
    while (true) {
    
    
    }
}

12.2 Difference between never and void

//void类型只是没有返回值 但本身不会出错
function Void():void {
    
    
	console.log();
}
 
//只会抛出异常没有返回值
function Never():never {
    
    
    throw new Error('aaa')
}

Difference 2: When we move the mouse up, we will find that there are only void and number

​ never will be removed directly in the union type

type A = void | number | never

12.3 Application scenarios of never type

It can be used for bottom-up logic.

type A = 'key1' | 'key2' | 'key3' 
 
function fn(value:A) {
    
    
   switch (value) {
    
    
       case "key1":
           break 
       case "key2":
          break 
       case "key3":
          break 
       default:
          break
   }
}

For example, if another colleague needs to add a new key4, then all Switch codes must be manually found and processed, otherwise bugs may be introduced.

Since any type cannot be assigned to a variable of neverthe type , when there is defaulta possibility of entering the branch, the type check of TS will help us find this problem in time.

type A = 'key1' | 'key2' | 'key3' | 'key4'
 
function fn(value:A) {
    
    
   switch (value) {
    
    
       case "key1":
           break 
       case "key2":
          break 
       case "key3":
          break 
       default:
          //是用于场景兜底逻辑
          const error:never = value;
          return error
   }
}

13 Symbol

Since ECMAScript 2015, symbol has become a new primitive type, just like number and string.

Values ​​of type symbol are created through the Symbol constructor.

// 可以传递参数作为唯一标识。只支持 number 、string两种类型以及 不传参。
let sym1 = Symbol();
let sym2 = Symbol(123);
let sym3 = Symbol('key');

Symbol values ​​are unique.

const s1 = Symbol();
const s2 = Symbol();
// s1 === s2 => false 内存地址并不同

key to use as object property

let sym = Symbol();
 
// 定义的 symbol 可以作为对象的属性名 
let obj = {
    
    
    [sym]: "value"
};
 
console.log(obj[sym]); // "value"

Attributes defined using symbols cannot be obtained through traversal as follows:

for in 、Object.keys 、getOwnPropertyNames、JSON.stringfy

const symbol1 = Symbol('666')
const symbol2 = Symbol('777')
const obj1= {
    
    
   [symbol1]: 'value1',
   [symbol2]: 'value2',
   age: 19,
   sex: '女'
}
// 1 for in 遍历
for (const key in obj1) {
    
    
   // 注意在console看key,是不是没有遍历到symbol1
   console.log(key)
}
// 2 Object.keys 遍历
Object.keys(obj1)
console.log(Object.keys(obj1))
// 3 getOwnPropertyNames
console.log(Object.getOwnPropertyNames(obj1))
// 4 JSON.stringfy
console.log(JSON.stringify(obj1))

It can be obtained by the following methods: Object.getOwnPropertySymbols(), Reflect.ownKeys()

// 1 拿到具体的symbol 属性,对象中有几个就会拿到几个
Object.getOwnPropertySymbols(obj1)
console.log(Object.getOwnPropertySymbols(obj1))
// 2 es6 的 Reflect 拿到对象的所有属性
Reflect.ownKeys(obj1)
console.log(Reflect.ownKeys(obj1))

Symbol.iterator iterator and generator for of

https://blog.csdn.net/qq1195566313/article/details/122463630?spm=1001.2014.3001.5501

14 Generics***

Generics, which define dynamic types.

14.1 Function generics

// 定义一个数字类型的函数
function num (a:number,b:number) : Array<number> {
    
    
    return [a ,b];
}
num(1,2)

// 定义一个字符串类型的函数
function str (a:string,b:string) : Array<string> {
    
    
    return [a ,b];
}
str('独孤','求败')

The two functions are only different in type, and the functions implemented are the same. At this time, generics can be used to optimize .

// 函数名称后面跟一个:<参数名>
// 参数名可以随便定义,这里用 T 
function Add<T>(a: T, b: T): Array<T>  {
    
    
    return [a,b]
}

// 当使用这个函数的时候,把参数的类型传进去就可以(动态类型)
// 也是将类型写在函数名的后面 <number>
Add<number>(1,2)
Add<string>('1','2')

// 也可以简写,TS 会自动推断
Add(1,2)
Add('1','2')

You can also use different generic parameter names , as long as the number and usage can correspond.

function Sub<T,U>(a:T,b:U):Array<T|U> {
    
    
    const params:Array<T|U> = [a,b]
    return params
}

// 数量上和使用方式上对应
Sub<Boolean,number>(false,1)

14.2 Type aliases define generics

type A<T> = string | number | T

let a:A<boolean> = true
let a:A<boolean> = 'str'
let a:A<boolean> = 123456
let a:A<null> = null
let a:A<undefined> = undefined

14.3 Defining a generic interface

When declaring an interface, add one after the name:<参数>

interface MyInter<T> {
    
    
   (arg: T): T
}
 
function fn<T>(arg: T): T {
    
    
   return arg
}
// 使用的时候传递类型
let result: MyInter<number> = fn
 
result(123)

14.4 Object literal generics

let foo: {
    
    
    <T>(arg: T): T 
}
 
foo = function <T>(arg:T):T {
    
    
   return arg
}
 
foo(123)

14.5 Generic constraints

That is, the type is followed by an extends, followed by a constraint type.

// 期望在一个泛型的变量上面,获取其 length 参数。但是,有的数据类型是没有 length 属性的。
function getLegnth<T>(arg:T) {
    
    
  return arg.length
}

// 这个时候我们就可以使用 泛型约束。

Therefore, we have to constrain the generic type used, we constrain it to a type with a length attribute, here we will use interface

interface Len {
    
    
   length:number
}
 
function getLegnth<T extends Len>(arg:T) {
    
    
  return arg.length
}
 
getLegnth<string>('123')

14.6 Constraining objects with keyof

Which uses TS generics and generic constraints. First define the T type and use the extends keyword to inherit the subtype of the object type, then use the keyof operator to get all the keys of the T type, and its return type is a union type, and finally use the extends keyword to constrain the K type to be a keyof T union subtype of type

function prop<T, K extends keyof T>(obj: T, key: K) {
    
    
   return obj[key]
}
 
 
let o = {
    
     a: 1, b: 2, c: 3 }
 
prop(o, 'a') 
prop(o, 'd') //此时就会报错发现找不到

advanced usage

interface Data {
    
    
    name: string
    age: number
    sex: string
}

// for in
// for(let key in obj)
type Options<T extends object> = {
    
    
    //[Key in keyof T]?:T[Key]
   readonly [Key in keyof T]:T[Key]
}

type B = Options<Data>

14.7 Generic classes

The declaration method is similar to the function name followed by the definition <type>

Determine the type when using new Sub()

class Sub<T>{
    
    
   attr: T[] = [];
   add (a:T):T[] {
    
    
      return [a]
   }
}
 
let s = new Sub<number>()
s.attr = [1,2,3]
s.add(123)
 
let str = new Sub<string>()
str.attr = ['1','2','3']
str.add('123')

15 tsconfig.json file

This file is tsc --initgenerated with the command

Detailed configuration:

"compilerOptions": {
    
    
  "incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
  "tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
  "diagnostics": true, // 打印诊断信息 
  "target": "ES5", // 目标语言的版本
  "module": "CommonJS", // 生成代码的模板标准
  "outFile": "./app.js", // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中,即开启时应设置"module": "AMD",
  "lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
  "allowJS": true, // 允许编译器编译JS,JSX文件
  "checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
  "outDir": "./dist", // 指定输出目录
  "rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
  "declaration": true, // 生成声明文件,开启后会自动生成声明文件
  "declarationDir": "./file", // 指定生成声明文件存放目录
  "emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
  "sourceMap": true, // 生成目标文件的sourceMap文件
  "inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
  "declarationMap": true, // 为声明文件生成sourceMap
  "typeRoots": [], // 声明文件目录,默认时node_modules/@types
  "types": [], // 加载的声明文件包
  "removeComments":true, // 删除注释 
  "noEmit": true, // 不输出文件,即编译后不会生成任何js文件
  "noEmitOnError": true, // 发送错误时不输出任何文件
  "noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
  "importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
  "downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
  "strict": true, // 开启所有严格的类型检查
  "alwaysStrict": true, // 在代码中注入'use strict'
  "noImplicitAny": true, // 不允许隐式的any类型
  "strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
  "strictFunctionTypes": true, // 不允许函数参数双向协变
  "strictPropertyInitialization": true, // 类的实例属性必须初始化
  "strictBindCallApply": true, // 严格的bind/call/apply检查
  "noImplicitThis": true, // 不允许this有隐式的any类型
  "noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
  "noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
  "noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
  "noImplicitReturns": true, //每个分支都会有返回值
  "esModuleInterop": true, // 允许export=导出,由import from 导入
  "allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
  "moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
  "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
  "paths": {
    
     // 路径映射,相对于baseUrl
    // 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
    "jquery": ["node_modules/jquery/dist/jquery.min.js"]
  },
  "rootDirs": ["src","out"], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
  "listEmittedFiles": true, // 打印输出文件
  "listFiles": true// 打印编译的文件(包括引用的声明文件)
}
 
// 指定一个匹配列表(属于自动指定该路径下的所有ts相关文件)
"include": [
   "src/**/*"
],
// 指定一个排除列表(include的反向操作)
 "exclude": [
   "demo.ts"
],
// 指定哪些文件使用该配置(属于手动一个个指定文件)
 "files": [
   "demo.ts"
]

Introduce a few commonly used ones:

1. Include
specifies that the compiled file defaults to compiling all ts files in the current directory

2.exclude
specifies the excluded files

3. target
specifies the version of compiled js such as es5 es6

4. Whether allowJS
allows compiling js files

5. Whether removeComments
deletes comments in the file during compilation

6. rootDir
directory of compiled files

7.outDir
output directory

8. sourceMap
code source file

9. strict
strict mode

10.module
default common.js optional es6 mode amd umd etc.

16 namespace namespace

Pollution caused by global variables cannot be avoided at work, and typescript provides namespace to avoid this problem.

1 Internal space, mainly used to organize code and avoid naming conflicts

2 Classes in the namespace, private by default

3 exposed through export

4 Defined by the namespace keyword

TypeScript is the same as ECMAScript 2015, any file that contains a top-level importor exportis considered a module. Conversely, if a file does not have a top-level importor exportdeclaration, its contents are considered globally visible (and thus visible to the module as well).

In the namespace, export the part you want to expose through export, otherwise you cannot read its value.

namespace a {
    
    
    export const Time: number = 1000
    export const fn = <T>(arg: T): T => {
    
    
        return arg
    }
    fn(Time)
}
 
 
namespace b {
    
    
     export const Time: number = 1000
     export const fn = <T>(arg: T): T => {
    
    
        return arg
    }
    fn(Time)
}
 
a.Time
b.Time

nested namespace

namespace a {
    
    
    export namespace b {
    
    
        export class Vue {
    
    
            parameters: string
            constructor(parameters: string) {
    
    
                this.parameters = parameters
            }
        }
    }
}
 
let v = a.b.Vue
 
new v('1')

Extract the namespace

// a.ts
export namespace V {
    
    
    export const a = 1
}

// b.ts   
import {
    
    V} from '../observer/index'
console.log(V);  //{a:1}

Simplify the namespace

namespace A  {
    
    
    export namespace B {
    
    
        export const C = 1
    }
}
 
import X = A.B.C
 
console.log(X);

merge namespace

// 重名的命名空间会合并
namespace a {
    
    
  export const b = 123
}

namespace a {
    
    
  export const c = 456
}
    
a.b
a.c

17 triple slash instruction

The triple slash reference can be understood as import, which tells the compiler the additional files that need to be introduced during the compilation process.

/// <reference path="..." />directive is the most common type of triple-slash directive. It is used to declare the dependencies built by the file.

For example:

// a.ts
namespace A {
    
    
    export const fn = () => 'a'
}
    
// b.ts
namespace B {
    
    
    export const fn = () => 'a'
}

// index.ts 引入之后直接可以使用变量A    
///<reference path="./index2.ts" />
///<reference path="./index3.ts" />
 
console.log(A);

Declaration file import

For example, introducing /// into the declaration file indicates that this file uses the name declared in @types/node/index.d.ts; and, this package needs to be included together with the declaration file during the compilation phase.

Use this directive only if you need to write a d.ts file.

///<reference types="node" />

18 declaration file d.ts

statement filedeclare

When using a third-party library, we need to reference its declaration file in order to obtain the corresponding code completion, interface prompt and other functions.

declare var 声明全局变量
declare function 声明全局方法
declare class 声明全局类
declare enum 声明全局枚举类型
declare namespace 声明(含有子属性的)全局对象
interface  type 声明全局类型
/// <reference /> 三斜线指令

If some third-party packages do not have declaration files, we can define them ourselves.

name.d.ts Create a file to declare

For example:

// index.ts
import express from 'express'
const app = express()
const router = express.Router()
app.use('/api', router)
router.get('/list', (req, res) => {
    
    
    res.json({
    
    
        code: 200
    })
})
 
app.listen(9001,()=>{
    
    
    console.log(9001)
})
// express.d.ts
declare module 'express' {
    
    
    interface Router {
    
    
        get(path: string, cb: (req: any, res: any) => void): void
    }
    interface App {
    
    
 
        use(path: string, router: any): void
        listen(port: number, cb?: () => void): void
    }
    interface Express {
    
    
        (): App
        Router(): Router
 
    }
    const express: Express
    export default express
}

19 Mixins

TypeScriptMix in MixinsIn fact, vue also has mixins, which you can regard as a merger.

19.1 Object mixins

ES6's Object.assign merges multiple objects

At this point people will be inferred as a cross type Name & Age & sex

interface Name {
    
    
    name: string
}
interface Age {
    
    
    age: number
}
interface Sex {
    
    
    sex: number
}
 
let people1: Name = {
    
     name: "小满" }
let people2: Age = {
    
     age: 20 }
let people3: Sex = {
    
     sex: 1 }
 
const people = Object.assign(people1,people2,people3)

19.2 Mixins of classes

First declare two mixins classes

class A {
    
    
    type: boolean = false;
    changeType() {
    
    
        this.type = !this.type
    }
}
 
class B {
    
    
    name: string = '张三';
    getName(): string {
    
    
        return this.name;
    }
}

Let's create a class that combines these two mixins:

The first thing you should notice is that instead of using extends implements, you use the class as an interface.

We can do this to achieve our goal, creating placeholder properties for the property methods that will be mixed in. This tells the compiler that these members are available at runtime. In this way, you can use the convenience brought by mixins, although you need to define some placeholder properties in advance.

class C implements A,B{
    
    
    type:boolean
    changeType:()=>void;
    name: string;
    getName:()=> string
}

Finally, create this helper function to help us do the mixin operation. It will traverse all the properties on the mixins and copy them to the target, replacing the previous placeholder properties with the real implementation code.

Object.getOwnPropertyNames() can get the properties of the object itself, remove its inherited properties, and traverse all its properties. It is an array, and traverse all its property names

Mixins(C, [A, B])
function Mixins(curCls: any, itemCls: any[]) {
    
    
    itemCls.forEach(item => {
    
    
        Object.getOwnPropertyNames(item.prototype).forEach(name => {
    
    
            curCls.prototype[name] = item.prototype[name]
        })
    })
}

20 Decorator Decorator

21 webPack build TS project

22 Actual combat: TS writes publish and subscribe mode

/*
	on	订阅/监听
	emit 发布/注册
	once 只执行一次
	off  解除绑定
*/

interface EventFace {
    
    
    on: (name: string, callback: Function) => void,
    emit: (name: string, ...args: Array<any>) => void,
    off: (name: string, fn: Function) => void,
    once: (name: string, fn: Function) => void
}
 
interface List {
    
    
    [key: string]: Array<Function>,
}
class Dispatch implements EventFace {
    
    
    list: List
    constructor() {
    
    
        this.list = {
    
    }
    }
    on(name: string, callback: Function) {
    
    
        const callbackList: Array<Function> = this.list[name] || [];
        callbackList.push(callback)
        this.list[name] = callbackList
    }
    emit(name: string, ...args: Array<any>) {
    
    
        let evnetName = this.list[name]
        if (evnetName) {
    
    
            evnetName.forEach(fn => {
    
    
                fn.apply(this, args)
            })
        } else {
    
    
            console.error('该事件未监听');
        }
    }
    off(name: string, fn: Function) {
    
    
        let evnetName = this.list[name]
        if (evnetName && fn) {
    
    
            let index = evnetName.findIndex(fns => fns === fn)
            evnetName.splice(index, 1)
        } else {
    
    
            console.error('该事件未监听');
        }
    }
    once(name: string, fn: Function) {
    
    
        let decor = (...args: Array<any>) => {
    
    
            fn.apply(this, args)
            this.off(name, decor)
        }
        this.on(name, decor)
    }
}
const o = new Dispatch()
 
 
o.on('abc', (...arg: Array<any>) => {
    
    
    console.log(arg, 1);
})
 
o.once('abc', (...arg: Array<any>) => {
    
    
    console.log(arg, 'once');
})
// let fn = (...arg: Array<any>) => {
    
    
//     console.log(arg, 2);
// }
// o.on('abc', fn)
// o.on('ddd', (aaaa: string) => {
    
    
//     console.log(aaaa);
// })
//o.off('abc', fn)
 
o.emit('abc', 1, true, '小满')
 
o.emit('abc', 2, true, '小满')
 
// o.emit('ddd', 'addddddddd')

23 TS advanced usage: proxy & Reflect

24 TS advanced usage: Partial & Pick

Guess you like

Origin blog.csdn.net/qq_53931766/article/details/130723957