欢迎光临散文网 会员登陆 & 注册

TS 从入门到深度掌握,晋级TypeScript高手吾爱青铜神树

2023-03-19 14:49 作者:bili_28881155651  | 我要投稿

TypeScript 核心概念梳理

下栽地止https://lexuecode.com/6655.html


什么是 TypeScript


简单的说 TypeScript 是 JavaScript 一个超集,能够编译成 JavaScript 代码


其核心能力是在代码编写过程中提供了类型支持,以及在编译过程中进行类型校验


先说一下 JS 的现状:
1、在 JS 中的变量本身是没有类型,变量可以接受任意不同类型的值,同时可以访问任意属性,属性不存在无非是返回 undefined
2、JS 也是有类型的,但是 JS 的类型是和值绑定的,是值的类型,用 typeof 判断变量类型其实是判断当前值的类型
// JavaScript

var a = 123
typeof a // "number"
a = 'sdf'
typeof a // "string"
a = { name: 'Tom' }
a = function () {
return true
}
a.xxx // undefined
复制代码
TS 做的事情就是给变量加上类型限制
1、限制在变量赋值的时候必须提供类型匹配的值
2、限制变量只能访问所绑定的类型中存在的属性和方法
举个简单的例子,如下是一段能够正常执行的 JS 代码:
let a = 100
if (a.length !== undefined) {
console.log(a.length)
} else {
console.log('no length')
}
复制代码
直接用 TS 来重写上面的代码,把变量 a 的类型设置为 number
在 TS 中给变量设置类型的语法是 【 : Type 】 类型注解
let a: number = 100
if (a.length !== undefined) { // error TS2339: Property 'length' does not exist on type 'number'.
console.log(a.length)
} else {
console.log('no length')
}
复制代码
但是如果直接对这个 TS 代码进行编译会报错,因为当变量被限制了类型之后,就无法访问该类型中不存在的属性或方法。
那再来写一段能正常执行的 TS
let a: string = 'hello'
console.log(a.length)
复制代码
编译成 JS 后的代码为
var a = 'hello'
console.log(a.length)
复制代码
可以发现 : string 这个类型限制编译之后是不存在的,只在编译时进行类型校验。
当 TS 源码最终被编译成 JS 后,是不会产生任何类型代码的,所以在运行时自然也不存在类型校验。
也就是说,假设一个项目,用 TS 来写,哼哧哼哧加上各种类型检验,项目测试通过部署到线上之后
最后运行在客户端的代码和我直接用 JS 来写的代码是一样的,写了很多额外的类型代码,竟然是为了保证能顺利编译成原来的代码
TypeScript 的作用
那 TS 的作用究竟是什么呢,主要是以下三点:
1、将类型系统看作为文档,在代码结构相对复杂的场景中比较适用,本质上就是良好的注释。
2、配合 IDE,有更好的代码自动补全功能。
3、配合 IDE,在代码编写的过程中就能进行一些代码校验。例如在一些 if 内部的类型错误,JS 需要执行到了对应代码才能发现错误,而 TS 在写代码的过程中就能发现部分错误,代码交付质量相对高一些,不过对于逻辑错误,TS 当然也是无法识别的。
TypeScript 类型梳理
分两类来介绍 TS 的类型系统:
1、JS 中现有的值类型在 TS 中对应如何去限制变量
2、TS 中拓展的类型,这些类型同样只在编译时存在,编译之后运行时所赋的值其实也是 JS 现有的值类型
下文中会穿插一些类似 [ xx ] 这样的标题,这是在列举介绍 TS 类型的过程中插入介绍的 TS 概念
JS 中现有的值类型如何绑定到变量

使用语法:类型注解【 : Type 】

布尔值
let isDone: boolean = false
复制代码
数值
let age: number = 18
复制代码
字符串
let name: string = 'jiangmo'
复制代码
空值
function alertName(): void { // 用 : void 来表示函数没有返回值
alert('My name is Tom')
}
复制代码
Null 和 Undefined
let u: undefined = undefined
let n: null = null
// 注意:和所有静态类型的语言一样,TS 中不同类型的变量也无法相互赋值
age = isDone // error TS2322: Type 'false' is not assignable to type 'number'.
// 但是因为 undefined 和 null 是所有类型的子类型,所以可以赋值给任意类型的变量
age = n // ok
复制代码
[ 类型推论 ]

如果没有明确的指定类型,那么 TypeScript 会依照类型推论的规则推断出一个类型

例如:定义变量的时候同时进行赋值,那么 TS 会自动推断出变量类型,无需类型注解
let age = 18
// 等价于
let age: number = 18
// 所以上面代码中的类型声明其实都可以省略
// 但是如果定义的时候没有赋值,不管之后有没有赋值,则这个变量完全不会被类型检查(被推断成了 any 类型)
let x
x = 'seven'
x = 7
// 所以这个时候应该显示的声明类型
let x: number
x = 7
复制代码
继续列举类型
数组的类型

语法是 【 Type[] 】

let nameList: string[] = ['Tom', 'Jerry']
let ageList: number[] = [5, 6, 20]
复制代码
对象的类型

接口 (interface) 用于描述对象的类型

interface Person { // 自定义的类型名称,一般首字母大写
name: string
age: number
}
let tom: Person = {
name: 'Tom',
age: 25,
}
复制代码
函数的类型

以函数表达式为例 ( 函数声明定义的函数也是用类似的 参数注解 语法来进行类型约束 )

// JavaScript

const sum = function (x, y) {
return x + y
}
复制代码
TS 中有多种语法来定义函数类型

直接约束出入参类型

const sum = function (x: number, y: number): number {
return x + y
}
复制代码

单独给 sum 变量设置类型

const sum: (x: number, y: number) => number = function (x, y) {
return x + y
}
复制代码
这里如果把函数类型直接提取出来用并起一个自定义的类型名,代码会更美观,也易复用。
利用 类型别名 可以给 TS 类型重命名
[ 类型别名 ]

类型别名的语法是 【 type 自定义的类型名称 = Type 】

type MySum = (x: number, y: number) => number
const sum: MySum = function (x, y) {
return x + y
}
复制代码
回到函数类型
1、用接口定义函数的类型
interface MySum {
(a: number, b: number): number
}
const sum: MySum = function (x, y) {
return x + y
}
复制代码
函数类型介绍完了,最后额外补充一下函数类型怎么 定义剩余参数的类型 以及 如何设置默认参数。
const sum = function (x: number = 1, y: number = 2, ...args: number[]): number {
return x + y
}
复制代码
类的类型

和函数类型的语法相似,直接在 ES6 语法中用【 : Type 】类型注解 和 参数注解 语法给类的属性和方法设置类型

class Animal {
name: string // 这一行表示声明实例属性 name
constructor(name: string) {
this.name = name
}
sayHi(): string {
return `My name is ${this.name}`
}
}
let a: Animal = new Animal('Jack') // : Animal 约束了变量 a 必须是 Animal 类的实例
console.log(a.sayHi()) // My name is Jack
复制代码
顺便值得一提的是,除了类型支持以外,TS 也拓展了 class 的语法特性
新增了三种访问修饰符 public、 private、 protected 和只读属性关键字 readonly 以及 abstract 抽象类
这里就不展开了,有需要的再去查阅一下官方文档即可
内置对象和内置方法
JavaScript 中有很多内置对象和工具函数,TS 自带其对应的类型定义

很多内置对象可以直接在 TypeScript 中当做定义好了的类型来使用

let e: Error = new Error('Error occurred')
let d: Date = new Date()
let r: RegExp = /[a-z]/
let body: HTMLElement = document.body
复制代码

一些内置的方法,TS 也补充了类型定义,配合 IDE 在编写代码的时候也能得到 TS 的参数提示。

Math.pow(2, '3') // error TS2345: Argument of type '"3"' is not assignable to parameter of type 'number'.
复制代码
TS 中拓展的类型
任意值 any
与其说 any 是 JS 中不存在的类型,不如说原本 JS 中的变量只有一个类型就是 any
任意值 any 的特点:
any 类型的变量可以赋值给任何别的类型,这一点和 null 与 undefined 相同任何类型都可以赋值给 any 类型的变量
在任意值上访问任何属性都是允许的
let a: any = 123
a = '123' // ok
let n: number[] = a // ok
a.foo && a.foo() // ok
复制代码
所以 any 是万金油,也是和 TS 进行类型约束的目的是相违背的,要尽量避免使用 any。
联合类型

类型中的或操作,在列出的类型里满足其中一个即可

let x: string | number = 1
x = '1'
复制代码
不过联合类型有一个额外约束:
当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法。
let x: string | number = 1
x = '1'
x.length // 这里能访问到 length ,因为 TS 能确定此时 x 是 string 类型
// 下面这个例子就会报错
function getLength(something: string | number): number {
return something.length // error TS2339: Property 'length' does not exist on type 'string | number'.
}
复制代码
两种解决思路

让 TS 能够自行推断出具体类型

function getLength(something: string | number): number {
if (typeof something === 'string') { // TS 能识别 typeof 语句
return something.length // 所以在这个 if 分支里, something 的类型被推断为 string
} else {
return 0
}
}
复制代码
利用 类型断言,手动强制修改现有类型
function getLength(something: string | number): number {
return (something as string).length // 不过这样做实际上代码是有问题的,所以用断言的时候要小心
}
复制代码
[ 类型断言 ]

用来手动指定一个值的类型,语法 【 value as Type 】

用类型断言修改类型时的限制:
1、联合类型可以被断言为其中一个类型
2、父类可以被断言为子类
3、任何类型都可以被断言为 any
4、any 可以被断言为任何类型
总结成一条规律就是:要使得 A 能够被断言为 B,只需要 A 兼容 B 或 B 兼容 A 即可。
双重断言

利用利用上述 3 和 4 两条规则,可以强制把一个值改为任意其他类型

let a = 3
(a as any) as string).split // ok
复制代码
如果说断言有风险,那双重断言就是在反复横跳了
字符串字面量类型
用来约束取值只能是某几个字符串中的一个
type EventNames = 'click' | 'scroll' | 'mousemove'
function handleEvent(ele: Element, event: EventNames) {
// do something
}
复制代码
注意,只有一个字符串也是字符串字面量类型
type MyType = 'hello'
复制代码
虽然一般不会手动设置这样的类型,不过类型推论经常会推断出这种类型。
比如某次编译报错提示为:Argument of type '"foo"' is not assignable to parameter of type 'number'.
提示中的 type '"foo"' 一般就是根据字符串 'foo' 推断出来的字符串字面量类型。
元组

类似 Python 中的元组,可以看做是固定长度和元素类型的数组

let man: [string, number] = ['Tom', 25]

// 不过 TS 中的元组支持越界
// 当添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型
man.push('male')
复制代码
枚举

用于取值被限定在一定范围内的场景,可以替代 JS 中用字面量来定义一个对象作为字典的场景

enum Directions {
Up,
Down,
Left,
Right,
}
let d: Directions = Directions.Left
复制代码
这里看到 Directions.Left 直接把类型当做一个值来用了。
不是说类型是用于【 : Type 】类型注解 语法来约束变量,编译之后类型代码都会被删除吗?
为了解释这个问题,我们先来来看看单纯的类型代码会被编译成什么。

首先以一个联合类型举例

type MyType = string | number | boolean
复制代码
编译结果:
// 不会产生任何 JS 代码
复制代码

再来看看枚举类型会被编译成什么

enum Directions {
Up,
Down,
Left,
Right,
}
console.log(Directions)
复制代码
编译结果:
var Directions
;(function (Directions) {
Directions[(Directions['Up'] = 0)] = 'Up'
Directions[(Directions['Down'] = 1)] = 'Down'
Directions[(Directions['Left'] = 2)] = 'Left'
Directions[(Directions['Right'] = 3)] = 'Right'
})(Directions || (Directions = {}))
console.log(Directions)
/*
运行时 log 出来的 Directions 变量如下
{
'0': 'Up',
'1': 'Down',
'2': 'Left',
'3': 'Right',
Up: 0,
Down: 1,
Left: 2,
Right: 3
}
*/
复制代码
这怎么理解呢?
let d: Directions = Directions.Left
复制代码
其实这一行代码中,前一个 Directions 表示类型,后一个 Directions 表示值。
即 Directions 是一个值和类型的“复合体”,在不同的语法中具象化为值或者类型。
其实有办法可以把类型部分从 Directions 中抽离出来。
enum Directions {
Up,
Down,
Left,
Right,
}
type MyDirections = Directions
console.log(MyDirections) // error TS2693: 'MyDirections' only refers to a type, but is being used as a value here.
复制代码
此时 MyDirections 就是一个纯粹的类型,不能当做一个值来使用。
其实之前介绍的函数类型、类类型等声明中,也存在这样的值与类型的“复合体”
const sum = function (x: number, y: number = 5): number {
return x + y
}
console.log(sum) // [Function: sum]
type MySum = typeof sum // 注意,剥离出来的函数类型是不会带有默认参数的,因为默认参数其实是函数的特性,和类型系统无关
const f: MySum = (a, b) => 3 // ok
console.log(MySum) // error TS2693: 'MySum' only refers to a type, but is being used as a value here.
复制代码
然后再回到枚举。
字符串枚举
用字符串字面量初始化枚举成员,在实际使用过程中很常见
enum Directions {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT',
}
console.log(Directions.Up === 'UP') // true
复制代码
常数枚举


用 const enum 定义的枚举类型


和普通枚举的区别就是对应的值也会在编译阶段被删除,只会留下枚举成员的值


const enum Directions {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT',
}
let d = Directions.Left

// 如果取消注释下面这行代码,编译会报错
// console.log(Directions) // error TS2475: 'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment or type query.
复制代码
编译结果:
var d = 'LEFT' /* Left */
复制代码
泛型


其实泛型并不是一种具体的类型,而是在定义函数、接口或类的类型时的的拓展特性。


泛型是类型系统里的 “函数” ,通过传入具体的 类型参数 来得到一个具体的类型,从而达到复用类型代码的目的


假设一个场景,某个函数的入参类型为 number | string ,并且出参类型和入参相同
先尝试用联合类型来约束出入参
type MyFunc = (x: number | string) => number | string
复制代码
但是 MyFunc 无法表示出参类型和入参相同,即入参是 number 的时候出参也是 number。
在这个场景下,可以利用泛型来定义出多个类似的函数类型。
泛型函数


表示声明了一个 类型参数,在定义类型的时候 T 就可以作为一个类型来使用


类型参数也可以定义多个,比如 <A, B, C>


function GenericFunc<T>(arg: T): T {
return arg
}
// 这里的 GenericFunc<number> 是表示的是一个函数值,同时将类型参数 T 赋值为 number
let n = GenericFunc<number>(1) // n 可以通过类型推论得出类型为 :number
// 进一步,利用 泛型约束 ,限制出入参为 number | string
type MyType = number | string
function GenericFunc<T extends MyType>(arg: T): T { // extends MyType 表示类型参数 T 符合 MyType 类型定义的形状
return arg
}
let s = GenericFunc<string>('qq')
let b = GenericFunc<boolean>(false) // error TS2344: Type 'boolean' does not satisfy the constraint 'string | number'.
复制代码
泛型接口

用 泛型接口 来定义函数类型

interface GenericFn<T> {
(arg: T): T
}
// 定义一个泛型函数作为函数实现
function identity<T>(arg: T): T {
return arg
}
// 使用泛型时传入一个类型来使 类型参数 变成具体的类型
// <number> 表示 T 此时就是 number 类型,GenericFn<number> 类似是 “函数调用” 并返回了一个具体的类型 (这里是一个函数类型)
const myNumberFn: GenericFn<number> = identity
const myStringFn: GenericFn<string> = identity
let n = myNumberFn(1) // n 可以通过类型推论得出类型为 :number
let s = myStringFn('string') // s 可以通过类型推论得出类型为 :string
复制代码
对比上述的 泛型函数 和 泛型接口,有一个区别:

给泛型函数传参之后得到的是一个函数值,而不是类型

// GenericFunc 是上面定义的泛型函数
type G = GenericFunc<string> // error TS2749: 'GenericFunc' refers to a value, but is being used as a type here.
复制代码

而泛型接口传参之后得到的是一个类型,而不是函数值

// GenericFn 是上面定义的泛型接口
type G = GenericFn<number> // ok
GenericFn<number>() // error TS2693: 'GenericFn' only refers to a type, but is being used as a value here.
复制代码
泛型类

用 泛型类 来定义类的类型

class GenericClass<T> {
zeroValue: T
constructor(a: T) {
this.zeroValue = a
}
}
let instance = new GenericClass<number>(1)
// 等价于
let instance: GenericClass<number> = new GenericClass(1)
// 因为有类型推论,所以可以简写成
let instance = new GenericClass(1)
复制代码
内置的数组泛型

TS 中内置类一个数组泛型 Array,传入类型参数后会返回对应的数组类型

// 数组的类型之前是用 【 Type[] 】 语法来表示的
let list: number[] = [1, 2, 3]
// 现在也可以这么表示
let list: Array<number> = [1, 2, 3]
复制代码
[ 声明合并 ]
上面那个场景,某个函数的入参类型为 number | string ,并且出参类型和入参相同,其实不用泛型也可以用函数重载来实现
函数的合并


即函数声明的合并,即函数重载


TS 中的重载并不是真正意义上的重载,只是在根据不同的实参类型,从上而下挑选出一个具体的函数类型来使用


function func(x: number): number
function func(x: string): string
function func(x: any): any {
// 这里定义的函数类型 (x: any): any 会被覆盖失效
return x.length
}
let n = func(1) // n 可以通过类型推论得出类型为 :number
let s = func('1') // s 可以通过类型推论得出类型为 :string
// 需要注意的是,如上重载之后只剩下两种函数类型,调用时的入参要么是 number 要么是 string,无法传入其他类型的值
let b = func(true) // error
/*
- error TS2769: No overload matches this call.
Overload 1 of 2, '(x: number): number', gave the following error.
Argument of type 'true' is not assignable to parameter of type 'number'.
Overload 2 of 2, '(x: string): string', gave the following error.
Argument of type 'true' is not assignable to parameter of type 'string'.
*/
复制代码
接口的合并

接口中方法的合并和函数的合并相同,但是 属性的合并要求类型必须唯一

interface Alarm {
price: number
alert(s: string): string
}
interface Alarm {
weight: number
alert(s: string, n: number): string
}
// 相当于
interface Alarm {
price: number
weight: number
alert(s: string): string
alert(s: string, n: number): string
}

运用 TS 手写 Promise 源码

主要实现功能:

resolve
reject
Promise.then
Promise.catch
Promise.all
Promise.reac

思路和上篇文章一致:

设定三个状态 PENDING、FULFILLED、REJECTED,只能由PENDING改变为FULFILLED、REJECTED,并且只能改变一次
MyPromise接收一个函数executor,executor有两个参数resolve方法和reject方法
resolve将PENDING改变为FULFILLED
reject将PENDING改变为FULFILLED
promise变为FULFILLED状态后具有一个唯一的value
promise变为REJECTED状态后具有一个唯一的reason

先申明部分常用类型
interface Resolve {
(value: unknown): void
}
interface Reject {
(reason: unknown): void
}
interface Exector {
(resolve: Resolve, reject: Reject): void
}
interface CallBackFn {
(): void
}
interface FulfilledFn {
(value: unknown): unknown
}
interface RejectedFn {
(reason: unknown): unknown
}
复制代码
class封装和js大同小异,不过加了一些类型定义,不同的是把state、value、reason等变量定义由构造函数内部移到了class,使用private声明为私有属性
class myPromiseForTS {
// 1、设定3个状态值,只能由PENDING转变成FULFILLED或REJECTED,且只能转变一次。
#PENDING: string = 'pending'
#FULFILLED: string = 'fulfilled'
#REJECTED: string = 'rejected'

private state: string = this.#PENDING // 状态存储
private value: unknown = null // resolve方法返回的唯一值
private reason: unknown = null // reject方法返回的唯一值
private onFulfilledCallBacks: CallBackFn[] = [] // fulfilled状态的回调数组
private onRejectedCallBacks: CallBackFn[] = [] // rejected状态的回调数组

// 2、创建构造函数,传入一个执行器函数
constructor(exector?: Exector) {
const resolve: Resolve = (value: unknown): void => {
if (this.state === this.#PENDING) {
this.state = this.#FULFILLED
this.value = value
this.onFulfilledCallBacks.forEach((fun: CallBackFn): void => {
fun()
})
}
}
const reject: Reject = (reason: unknown): void => {
if (this.state === this.#PENDING) {
this.state = this.#REJECTED
this.reason = reason
this.onRejectedCallBacks.forEach((fun: CallBackFn): void => {
fun()
})
}
}
// && 确保exector方法存在再执行,避免ts报错, 下面同理
try {
exector && exector(resolve, reject)
} catch (err: unknown) {
reject(err)
}
}

// then方法
// 通过settimeOut来模拟异步调用,保证链式调用,所以then方法抛出的是一个新的promis,并将返回值进行resolve
then(onFulfilled: FulfilledFn | null, onRejected?: RejectedFn): myPromiseForTS {
if (!onFulfilled || typeof onFulfilled !== 'function') {
onFulfilled = (value: unknown): unknown => value
}
if (!onRejected || typeof onRejected !== 'function') {
onRejected = (reason: unknown): unknown => reason
}
const myPromiseForTS_: myPromiseForTS = new myPromiseForTS((resolve: Resolve, reject: Reject) => {
switch (this.state) {
// PENDING状态时将回调方法全部存在到回调数组内
case this.#PENDING:
this.onFulfilledCallBacks.push(() => {
setTimeout(() => {
try {
const result: unknown = onFulfilled && onFulfilled(this.value)
resolve(result)
} catch (err: unknown) {
reject(err)
}
}, 0)
})
this.onRejectedCallBacks.push(() => {
setTimeout(() => {
try {
const result: unknown = onRejected && onRejected(this.reason)
resolve(result)
} catch (err: unknown) {
reject(err)
}
}, 0)
})
break
case this.#FULFILLED:
setTimeout(() => {
try {
const result: unknown = onFulfilled && onFulfilled(this.value)
resolve(result)
} catch (err) {
reject(err)
}
}, 0)
break
case this.#REJECTED:
setTimeout(() => {
try {
const result: unknown = onRejected && onRejected(this.reason)
reject(result)
} catch (err) {
reject(err)
}
}, 0)
break
}
})
return myPromiseForTS_
}

// catch方法
catch(onRejected: RejectedFn): myPromiseForTS {
return this.then(null, onRejected)
}

// finally方法
finally(fn: Function): myPromiseForTS {
return this.then((value) => {
fn()
return value
}, (reason) => {
fn()
throw reason
})
}

// all方法,接收一个promise数组,当所有promise状态都为resolve时执行resolve
all(promises: myPromiseForTS[]): myPromiseForTS {
return new myPromiseForTS((resolve, reject) => {
if (promises.length === 0) {
resolve([])
} else {
const result: unknown[] = [] // 接受promise返回值
for (let i: number = 0; i < promises.length; i++) {
promises[i].then(data => {
result[i] = data
if (++i === promises.length) {
resolve(result)
}
}, (err: unknown) => {
reject(err)
return
})
}
}
})
}

//reac方法,接收一个promise数组,当有一个promise状态为resolve时执行resolve
reac(promises: myPromiseForTS[]): myPromiseForTS {
return new myPromiseForTS((resolve, reject) => {
if (promises.length === 0) {
resolve(null)
} else {
for (let i: number = 0; i < promises.length; i++) {
promises[i].then(data => {
resolve(data)
}, reason => {
reject(reason)
return
})
}
}
})
}
}
复制代码
下面老规矩,测试方法和测试结果截图
function testFn(type: String): myPromiseForTS {
return new myPromiseForTS((resolve, reject) => {
switch (type) {
case 'resolve':
resolve('resolve is success !')
break
case 'reject':
reject('reject is success !')
break
case 'finallyResolve':
resolve('finallyResolve is success !')
break
case 'finallyReject':
reject('finallyReject is success !')
break
}
})
}

const tsFn1: myPromiseForTS = testFn('resolve')
tsFn1.then(e => {
console.log(e)
})
// 输出: resolve is success !


const tsFn2: myPromiseForTS = testFn('reject')
tsFn2.catch(e => {
console.log(e)
})
// 输出: 'reject is success !

const tsFn3: myPromiseForTS = testFn('finallyResolve')
tsFn3.then(e => {
console.log(e)
}).finally(() => {
console.log('finally is success !')
})
// 输出: finallyResolve is success !
// 输出: finally is success !

const tsFn4: myPromiseForTS = testFn('finallyReject')
tsFn4.catch(e => {
console.log(e)
}).finally(() => {
console.log('finally is success !')
})
// 输出: finallyReject is success !
// 输出: finally is success !

new myPromiseForTS().all([tsFn1, tsFn3]).then(e => {
console.log('promiseAll', e)
})
// 输出: promiseAll [ 'resolve is success !', 'finallyResolve is success !' ]

new myPromiseForTS().reac([tsFn1, tsFn2]).then(e => {
console.log('promissReac', e)
})
// 输出: promissReac resolve is success !


TS 从入门到深度掌握,晋级TypeScript高手吾爱青铜神树的评论 (共 条)

分享到微博请遵守国家法律