愿你的努力被世界看到!

优先使用接口(interface)定义对象结构,类型别名(type)用于联合类型或复杂类型。

元组

在 py 中元组是固定类型,固定长度的,但是在 ts 中只固定了类型,仍然通过 push 等方法来改变(设计如此)。

1
2
3
4
5
6
let t:[number, string] = [1, 'content'];
// t[0] = 'yes' // 报错
// t[2] = 4 // 报错
t.push(100)
// t.push(false) // 报错,不兼容 boolean
console.log(t) // [ 1, 'content', 100 ]

ts 4.0 新增加了命名元组,可以增强代码的可读性。

1
2
3
4
5
6
7
8
9
10
11
12
// 命名元组
type UserRecord = [id: number, name: string, email: string, createdAt: Date];

const users: UserRecord[] = [
[1, 'John', '<EMAIL>', new Date()],
[2, 'Jane', '<EMAIL>', new Date()],
[3, 'Bob', '<EMAIL>', new Date()],
];

for (const [id, name, email, createdAt] of users) {
console.log(id, name, email, createdAt);
}

unknown

不保证类型,但是能保证类型安全。

1
2
3
4
5
6
let r:unknown = 1
r = 2
r = false
console.log(r)
// r() 报错
// console.log(r.toString()) // 报错

如果使用 any 上面的代码就不会报错。unknown 需要做进一步的判断:

1
2
3
4
5
let r:unknown = 1
// unknown 类型不能直接调用,需要判断类型
if (typeof r === 'function') {
r()
}

never

never 表示永远不存在的值的类型,比如抛出异常或者死循环。表示永远不存在的值的类型,是任何类型的子类型,可以赋值给任何类型,除 never 本身外,没有类型可以赋值给 never

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 正确:never可以赋值给任何类型
let a: never;
let b: string = a; // OK

// 错误:其他类型不能赋值给never
let c: never = 123; // Error

function throwError(message: string): never {
throw new Error(message);
}

function whileTrue(): never {
while (true) { }
}

可以用于类型保护中的穷尽检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Shape = "circle" | "square" | "triangle";

function getArea(shape: Shape): number {
switch (shape) {
case "circle":
return Math.PI * 2 ** 2;
case "square":
return 4 * 4;
default:
// 如果Shape类型扩展但忘记处理新类型,这里会报错
const exhaustiveCheck: never = shape;
return exhaustiveCheck;
}
}

定义函数

在定义函数的时候推荐将函数的参数和返回值单独用一个 type 来进行定义,这样函数的定义可以做到复用。

1
2
3
4
5
6
7
8
9
// good
type Fun = (a: number) => boolean
const fun1: Fun = () => false
const fun3: Fun = () => false

// bad
function fun2(a: number): boolean {
return false
}

定义对象

虽然 type 也能完成定义,但是在定义对象类型或者类的结构的时候 interface 更适合。

1
2
3
4
// type Obj = { a: string, b: boolean }
interface Obj { a: string, b: boolean }

const obj1: Obj = {a:'hello', b:false}

type 和 interface 的区别

交叉类型 &,联合类型 |

特性 interface type
本质 结构定义 类型别名
扩展方式 可使用 extends 关键字扩展 一般不能直接扩展,但可通过交叉类型&间接扩展
重名处理 重名时会自动合并声明 重名会报错
与类的关系 可被类实现 不能被类实现
支持类型 主要用于对象类型,不支持联合类型和交叉类型 支持联合类型和交叉类型

实际开发的共识是:type 和 interface 都能用的时候用 interface。

索引类型和映射类型

对象的属性(key)叫做索引。映射类型就是使用已有类型创建新类型(通过操作符)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
interface Product {
name: string
price: number
}

interface Products {
// 这里的 id 只是一个占位
[id: number]: Product
}

const products: Products = {
1: {
name: 'nick',
price: 100,
},
2: {
name:'adidas',
price:200
}
}

// 常见操作符
type Keys = keyof Product // 'name' | 'price'
type Tp = typeof products[1]

泛型

条件类型和工具类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 定义函数返回值:string 类型返回 1, number 类型返回 0
type ReType<T> = T extends string ? 1 : 0

// 定义一个函数,函数的参数和返回值是外部传入的类型
type Func<T> = (a: T) => ReType<T>

const fun1: Func<string> = (a) => {
// a 是 string 类型,同时返回值只能是 1
return 1
}
const fun2: Func<number> = (a) => {
// a 是 number 类型,同时返回值只能是 0
return 0
}

类型编程的 4 个范式

  1. 访问性修饰工具:Partial, Required, Readonly
  2. 结构工具:Pick,Omit,Record
  3. 集合工具类型:NonNullable,Exclude
  4. 模式匹配工具类型:ReturnType