ts高级用法

typescript 的基本使用方式基本已经可以覆盖到我们绝大部门的使用场景,但是我们也任会碰到一些特殊的场景需要我们使用一些高级的ts的用法,下面总结一下我常用的一些用法。

keyof

keyof 能够帮助我们快速生成 interface 的 key 的类型。

1
2
3
4
5
6
7

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

type AKeys = keyof A; // age | name

利用 keyof 提取接口

有时候我们可能会碰到需要提取接口部门结构的需求,这时候我们可以利用 ts 提供的pick类型

1
2
3
4
5
6
7
8
9
10
11
12

type pick<T, K extends keyof T> = {
[key in K]: T[key]
}

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

type C = pick<A, 'age' | 'sex'>; // { age: number; sex: string; }

多态

常用的使用场景就是我们可能需要根据传入值的类型来确定返回值的类型

1
2
3
4
5
6

interface Create {
(val: number): number
(val: string): string
(val: Date): Date
}

有个典型的例子就是eventBus,我们可能需要在不同的事件监听不同的类型的数据和广播不同类型的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum EventName {
sayName,
run,
}
interface EventBusOn {
(event: EventName.sayName, cb: (params: {name: string}) => void): void
(event: EventName.run, cb: (params: {duration: number}) => void): void
}

interface EventBusEmit {
(event: EventName.sayName, params: {name: string}): void
(event: EventName.run, params: {duration: number}): void
}

interface EventBus {
on: EventBusOn;
emit: EventBusEmit;
}

const eventBus: EventBus = new Vue();
eventBus.on(EventName.sayName, ({name}) => {
console.log(name);
});
eventBus.emit(EventName.sayName, {name: 'sd'});

这样还有个问题,就是我们每次增加一个事件类型,都需要去修改 EventBusOnEventBusEmit 两个接口,但是他们的修改模式基本是固定的,结合前面的两个技巧,我们也可以优化这个接口。

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
enum EventName {
sayName,
run,
}
interface EventParams {
[EventName.sayName]: {
name: string;
};
[EventName.run]: {
duration: number;
};
}
interface EventBusOn {
<T extends EventName>(event: T, cb: (params: EventParams[T]) => void): void
}

interface EventBusEmit {
<T extends EventName>(event: T, params: EventParams[T]): void
}
interface EventBus {
on: EventBusOn;
emit: EventBusEmit;
}

const eventBus: EventBus = new Vue();
eventBus.on(EventName.sayName, ({name}) => {
console.log(name);
})
eventBus.emit(EventName.sayName, {name: 'name'})

利用 is 帮助类型推断

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 A {
type: string;
data: {
name: string;
}
}
interface B {
type: string;
data: boolean;
}
function isA(params: any) {
return params.type === 'A';
}
// 这里能够帮助推断出类型
function isA2(params: any): params is A {
return params.type === 'A';
}
function test(params: A | B) {
if (isA(params)) {
params.data
}
if ((isA2(params))) {
params.data
}
}

泛型的类型限制

1
2
3
4

interface A<T extends {length: number;}> {
data:T;
}

这里就会限制了类型A的data属性必须是有length属性,且length类型为number

infer 关键字

ts 官方是有提供相关的几个工具类型,也有用到 infer

1
2
3
4
5
6
7
8
9
10

/**
* Obtain the parameters of a constructor function type in a tuple
*/
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;

/**
* Obtain the return type of a function type
*/
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

这里我们可以通过 infer 关键字做类型推断,意思有点想,比方说 ReturnType 中我们如果传入的函数的返回值类型是R,则我们把R返回,这个关键字目前来看使用场景还比较少