TypeScript

课程目标

  • 掌握 TS 基础理论及原理
  • 熟练使用 TS
  • 了解 TS 的编译原理

TypeScript 是 JavaScript 的⼀个超集,⽀持 ECMAScript 6 标准

知识要点

Why

TypeScript 提供的静态类型系统,大大增强了代码的可读性以及可维护性;同时,它提供最新和不断发展的 JavaScript 特性,能让我们建立更健壮的组件。

TS 基本使用

基本概念

类型注解:TypeScript ⾥的类型注解是⼀种轻量级的为函数或变量添加约束的⽅式。

原始类型

JavaScript 原始类型也同样适应于 TypeScript 的类型系统,因此 stringnumberboolean 也可以被用作类型注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let num: number;
let str: string;
let bool: boolean;

num = 123;
num = 123.456;
num = '123'; // Error

str = '123';
str = 123; // Error

bool = true;
bool = false;
bool = 'false'; // Error

数组

TypeScript 为数组提供了专用的类型语法,因此你可以很轻易的注解数组。它使用后缀 [],接着你可以根据需要补充任何有效的类型注解(如::boolean[])。它能让你安全的使用任何有关数组的操作,而且它也能防止一些类似于赋值错误类型给成员的行为。如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
let boolArray: boolean[];

boolArray = [true, false];
console.log(boolArray[0]); // true
console.log(boolArray.length); // 2

boolArray[1] = true;
boolArray = [false, false];

boolArray[0] = 'false'; // Error
boolArray = 'false'; // Error
boolArray = [true, 'false']; // Error

元组类型

JavaScript 并不支持元组,开发者们通常只能使用数组来表示元组。而 TypeScript 支持它,开发者可以使用 :[typeofmember1, typeofmember2] 的形式,为元组添加类型注解,元组可以包含任意数量的成员,示例:

1
2
3
4
5
6
7
let nameNumber: [string, number];

// Ok
nameNumber = ['Jenny', 221345];

// Error
nameNumber = ['Jenny', '221345'];

特殊类型

Any Void Null 和 Undefined Never Object

any

any 类型在 TypeScript 类型系统中占有特殊的地位。它提供给你一个类型系统的「后门」,TypeScript 将会把类型检查关闭。在类型系统里 any 能够兼容所有的类型(包括它自己)。因此,所有类型都能被赋值给它,它也能被赋值给其他任何类型。

null 和 undefined

在类型系统中,JavaScript 中的 null 和 undefined 字面量和其他被标注了 any 类型的变量一样,都能被赋值给任意类型的变量,如下例子所示:

1
2
3
4
5
6
7
8
// strictNullChecks: false 关闭严格的 null 检查

let num: number;
let str: string;

// 这些类型能被赋予
num = null;
str = undefined;
void

使用 :void 来表示一个函数没有一个返回值

never

never 类型是 TypeScript 中的底层类型。它自然被分配的一些例子:

  • 一个从来不会有返回值的函数(如:如果函数内含有 while(true) {});
  • 一个总是会抛出错误的函数(如:function foo() { throw new Error('Not Implemented') }foo 的返回类型是 never);

你也可以将它用做类型注解:

1
let foo: never; // ok

但是,never 类型仅能被赋值给另外一个 never

1
2
3
4
5
6
let foo: never = 123; // Error: number 类型不能赋值给 never 类型

// ok, 作为函数返回类型的 never
let bar: never = (() => {
throw new Error('Throw my hands in the air like I just dont care');
})();

接口(interface)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// interface object
interface User {
name: string
age?: number
// readonly id: number
}
function printUser(user: User): void{
console.log(user.name, ':', user.age);
}
const user1: User = {
name: '小刘',
age: 23,
};
printUser(user1);

//interface function 函数类型
interface UserFun {
(name: string, age: number): void;
}

const myFunction:UserFun = (name, age)=>{
console.log('SetUser');
console.log(name, ':', age);
}

myFunction('1', 2);

类(class)

函数

泛型

枚举

1

迭代器和生成器

装饰器

类装饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
function Log(target:any) {
console.log(target);
console.log("in log decorator");
}

@Log
class A {
constructor(){
console.log("constructor")
// logger.log('ddd');
}
}
// new A(); // js 运行时装饰器执行,无需 new
方法装饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function GET(url: string) {
console.log('entry GET decorator');
return function (target:any, methodName: string, descriptor: PropertyDescriptor) {
console.log('entry GET decorator function');
!target.$Meta && (target.$Meta = {});
target.$Meta[methodName] = url;
}
}

class HelloService {
constructor() {
console.log("constructor")
}
@GET("xx")
getUser() {
console.log('getUser function called');
}
}
new HelloService().getUser();

// console.log((<any>HelloService).$Meta);
// console.log((new HelloService() as any).$Meta);
访问器饰器
方法参数饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
function PathParam(paramName: string) {
return function (target:any, methodName: string, paramIndex: number) {
!target.$Meta && (target.$Meta = {});
target.$Meta[paramIndex] = paramName;
}
}

class HelloService {
constructor() { }
getUser( @PathParam("userId") userId: string) { }
}

// console.log((<any>HelloService).prototype.$Meta); // {'0':'userId'}
属性装饰器

继承 多态 重载 重写

抽象类 & 抽象方法

TypeScript 编译原理

SourceCode(源码) ~~ 扫描器 ~~> Token 流 ~~ 解析器 ~~> AST(抽象语法树)~~ 绑定器 ~~> Symbols(符号)

Scanner 扫描仪

Parser 解析器

Binder 绑定器

Checker 检查器

Emitter 发射器

补充知识点

类型断言

type 与 interface 的异同

axios 封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
type Method = 'GET' | 'POST' | 'PUT' | 'DELETE'
type ResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'

interface AxiosRequest {
baseURL?: string;
url: string;
data?: any;
params?: any;
method?: Method;
headers?: any;
timeout?: number;
responseType?: ResponseType;
}

interface CustomResponse<T> {
readonly status: boolean;
readonly message: string;
data: T;
origin?: any;
}


import axios, { AxiosRequestConfig } from 'axios';

// 定义接口
interface PendingType {
url?: string;
method?: Method;
params: any;
data: any;
cancel: Function;
}

// 取消重复请求
const pending: Array<PendingType> = [];
const CancelToken = axios.CancelToken;

// axios 实例
const instance = axios.create({
timeout: 10000,
responseType: 'json'
});

// 移除重复请求
const removePending = (config: AxiosRequestConfig) => {
for (const key in pending) {
const item: number = +key;
const list: PendingType = pending[key];
// 当前请求在数组中存在时执行函数体
if (list.url === config.url && list.method === config.method && JSON.stringify(list.params) === JSON.stringify(config.params) && JSON.stringify(list.data) === JSON.stringify(config.data)) {
// 执行取消操作
list.cancel('操作太频繁,请稍后再试');
// 从数组中移除记录
pending.splice(item, 1);
}
}
};

// 添加请求拦截器
instance.interceptors.request.use(
(request:any) => {
// TODO: handle loading

removePending(request);
request.cancelToken = new CancelToken((c) => {
pending.push({ url: request.url, method: request.method, params: request.params, data: request.data, cancel: c });
});
return request;
},
(error: any) => {
return Promise.reject(error);
}
);
// 添加响应拦截器
instance.interceptors.response.use(
(response: any) => {
removePending(response.config);

const errorCode = response?.data?.errorCode;
switch (errorCode) {
case '401':
// 根据errorCode,对业务做异常处理(和后端约定)
break;
default:
break;
}

return response;
},
(error: any) => {
const response = error.response;

// 根据返回的http状态码做不同的处理
switch (response?.status) {
case 401:
// token失效
break;
case 403:
// 没有权限
break;
case 500:
// 服务端错误
break;
case 503:
// 服务端错误
break;
default:
break;
}

return Promise.reject(response || {message: error.message});
}
);

class BaseHttp {
// 外部传入的baseUrl
// @ts-ignore
protected baseURL: string = process.env.VUE_APP_BaseURL as string;
// 自定义header头
protected headers: object = {
ContentType: 'application/json;charset=UTF-8'
}

private apiAxios<T>({
baseURL = this.baseURL,
headers = this.headers,
method,
url,
data,
params,
responseType
}: AxiosRequest): Promise<CustomResponse<T>> {

return new Promise((resolve, reject) => {
instance({
baseURL,
headers,
method,
url,
params,
data,
responseType
}).then((res: any) => {
// 200:服务端业务处理正常结束
if (res.status === 200) {
// TODO ...
// resolve({});
} else {
resolve({
status: false,
message: res.data?.errorMessage || (url + '请求失败'),
data: null
});
}
}).catch((err: any) => {
const message = err?.data?.errorMessage || err?.message || (url + '请求失败');
// eslint-disable-next-line
reject({ status: false, message, data: null});
});
});
}

/**
* GET类型的网络请求
*/
protected getReq<T>({
baseURL,
headers,
url,
data,
params,
responseType
}: AxiosRequest) {
return this.apiAxios<T>({
baseURL,
headers,
method: 'GET',
url,
data,
params,
responseType
});
}

/**
* POST类型的网络请求
*/
protected postReq<T>({ baseURL, headers, url, data, params, responseType }: AxiosRequest) {
return this.apiAxios<T>({ baseURL, headers, method: 'POST', url, data, params, responseType });
}

/**
* PUT类型的网络请求
*/
protected putReq<T>({ baseURL, headers, url, data, params, responseType }: AxiosRequest) {
return this.apiAxios<T>({ baseURL, headers, method: 'PUT', url, data, params, responseType });
}

/**
* DELETE类型的网络请求
*/
protected deleteReq<T>({ baseURL, headers, url, data, params, responseType }: AxiosRequest) {
return this.apiAxios<T>({ baseURL, headers, method: 'DELETE', url, data, params, responseType });
}
}