图片

TypeScript环境搭建开始,整理TS开发常见要点


搭建 TS 编译环境

1
> npm install -g typescript

更多信息: TypeScript 中文网

项目根目录下执行

1
tsc --init

将会生成tsconfig.josn配置文件

默认输入目录为根目录

image-20201126102517222

根目录创建.ts 结尾文件,运行vscode自带命令可以编译成 js 文件

终端—>运行任务—>typescript—>监视 tsconfig.json 文件即可

image-20201126105437150

更改tsconfig.json的 rootDir 即可手动指定 ts 文件源目录

image-20201126105708272

重要概念

接口

任意类型

1
2
3
4
5
6
7
8
interface PlainObject {
[propName: string]: number;
}
let obj: PlainObject = {
x: 1,
y: 2,
d: 4,
};

接口的继承

1
2
3
4
5
6
7
8
9
10
interface Speakable {
speak(): void;
}
interface SpeakChinese extends Speakable {
speakChinese(): void;
}
class Person implements SpeakChinese {
speak() {}
speakChinese() {}
}

接口的 readonly

1
2
3
4
interface Cricle {
readonly PI: number;
radius: number;
}

可索引接口(对数组和对象进行约束)

1
2
3
4
5
6
7
8
9
interface UserInterface {
[index: number]: string;
}
let arr: UserInterface = ["1", "2", "3"];
let obj: UserInterface = {
1: "1",
2: "2",
3: "3",
};

泛型

  • 泛型相当于“函数参数”,在定义时候静态约束,在执行时候动态指定

典型应用:

1
2
3
4
5
6
7
8
9
10
11
namespace d {
function createArr<T>(length: number, val: T): Array<T> {
let result: Array<T> = [];
for (let i = 0; i < length; i++) {
result.push(val);
}
return result;
}
console.log(createArr<string>(3, "x"));
console.log(createArr<number>(3, 1));
}

接口泛型

1
2
3
4
5
6
7
8
9
10
namespace f {
// * 接口泛型测试
interface Calculate {
<T>(a: T, b: T): T;
}
let calc: Calculate = function <T>(a: T, b: T): T {
return a;
};
console.log(calc<number>(5, 5));
}

多个类型的接口泛型测试

1
2
3
4
5
6
// * 多个泛型类型接口测试
function swap<A, B>(tuple: [A, B]): [B, A] {
return [tuple[1], tuple[0]];
}
let result = swap(["李华", 20]);
console.log(result);

默认泛型

image-20201126120219869

泛型的约束(泛型继承自定义接口)

1
2
3
4
5
6
7
8
// * 泛型约束
interface LengthWise {
length: number;
}
function print<T extends LengthWise>(val: T): number {
return val.length;
}
console.log(print([1, 2, 3]), "xxx");

泛型接口

1
2
3
4
5
6
7
// * 泛型接口
interface Cart<T> {
list: T[];
}
let cart: Cart<string> = {
list: ["1", "2", "3"],
};

泛型类型别名的使用

image-20201126121510428

泛型接口泛型类型别名的区别

image-20201126121411081

结构类型系统

一.接口的兼容性

前置概念

鸭子类型:只要一个数据叫的像鸭子,不管长得啥样,就认为它是鸭子。

image-20201127195106980

1.基本类型的兼容性

由于a上有toString()方法,所有a可以复制给obj

1
2
3
4
5
let obj: {
toString(): string
}
let a = '123'
obj = a

2.类的兼容性

  • 父类的变量能够指向子类的实例,子类的变量不能指向父类的实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ? 类的兼容性
class Animal {
constructor(public name: string){}
}
class Bird extends Animal {
constructor(name: string, public swing: string){
super(name)
}
}
let c: Animal;
c = new Bird('this is a bird', 'i can swing') // * 可以,因为 c 拥有 name 属性即可
let d: Bird;
// d = new Animal('animal'); // ! 不行,因为 d 上缺少 swing 属性
d = {name: 'x', swing: 'y'}; // 可以

3.函数的兼容性

  • 函数的参数少可以,不能多
image-20201127200146129
  • 函数的返回值指定函数返回数据的结构,多可以,不能少

    image-20201127200252378

4.函数参数的协变

  • 函数的某个参数类型,多可以,不能少
image-20201127200502427

虽然可以将log1赋值给log ,但log的类型还是没有发生变化

image-20201127201413512

5.枚举的兼容性

image-20201127204950742

二、类型保护

  • 通过if判断,或者!.非空断言(如s!.toString())更精确地知道变量属于哪种类型
image-20201127205228804 image-20201127205305423
  • 通过if判断在联合类型中锁定类型

    image-20201127205724952
  • 自定义的类型保护

image-20201127210101685

联合类型和交叉类型

一、交叉类型

  • 交叉类型定义的变量必须实现所有属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace d {
interface Bird{
name: string;
fly(): void
}
interface Pig {
name: string;
eat(): void
}
// * 使用 type 定义交叉类型
type Animal = Bird & Pig
let a: Animal = {
name: '小猪佩奇',
fly(){},
eat(){}
}
}
  • 使用typeof 获取变量的类型
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
namespace d {
interface Bird{
name: string;
fly(): void
}
interface Pig {
name: string;
eat(): void
}
// * 使用 type 定义交叉类型
type Animal = Bird & Pig
let a: Animal = {
name: '小猪佩奇',
fly(){},
eat(){}
}
}

namespace e {
// * 使用 typeof 提取某一个变量的类型
let p = {
name: 'lihua',
age: 13
}
type Person = typeof p // ! 注意:定义类型只能用 type 不可以使用 let
let p2: Person = {
name: 'hanmeimei',
age: 20
}
}
  • 索引访问操作符,我们可以使用[]获取某一个接口的子类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace f {
interface Person {
name: string;
age: string;
job: {
work: string;
};
interests: {name: string; level: string}[]
}

let me: Person['job'] = {work: '编程'}
let you: Person['job']['work'] = 'IT'
let him: Person['interests'] = [{name: '阅读', level: '1'}]
}
  • keyof索引类型查询操作符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace g {
interface Person {
name: string;
age: number;
gender: 'male' | 'female'
}
type PersonKey = keyof Person
function getValByKey(obj: Person, key: PersonKey) {
return obj[key]
}
let p: Person = {
name: '李华',
age: 12,
gender: 'male'
}
}

二、Partial、Required、Pick、Readonly使用

  • 前置知识:in操作符可以遍历一个类型集合|Partial 可以将接口属性都变成可选类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace h {
interface Person {
name: string;
age: number;
gender: 'male' | 'female'
}
type Partial<T> = {
[key in keyof T]?: T[key]
}
type PartialPerson = Partial<Person>
let p1: PartialPerson = {
name: '李华' // * age 和 gender 没有也 OK
}
}
  • Pick 可以从已知接口中分离出某个属性组成新对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace h {
interface Person {
name: string;
age: number;
gender: 'male' | 'female'
}
type Pick<T, K extends keyof T> = {
[key in K]: T[K] // ! 不可以 K: T[K],因为 K 代表类型,由于继承自 keyof T,理论上是个 key 数组
}
type PickPerson = Pick<Person, 'name'>
let p2: PickPerson = {
name: '李华'
}
}

三、interface、type、class适用场景

image-20201203222852596