最近阅读了 lib.es5.d.ts 中的各种声明文件,里面涉及了一些官方文档中没有提到的高级类型用法和一些工具类,整理出来以便日后复习学习。

infer

类型推断,当我们不知道接受的参数是什么类型时候,可以采用infer来进行类型推断

1
2
eg:
type ParamType<T> = T extends (param: infer P) => any ? P : T;

如上,当我们不知道param是什么类型,可以采用infer推断。
以上整句表示为,当T符合(param: infer P) => any这个格式时候,返回param的类型,否则返回T的类型

1
2
eg:
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;

以上,整句表示为,如果泛型T,是一个函数,里面有一个this,符合这个,就会返回这个未知类型(通过infer推测的类型),否则返回unknown

ThisType

使用传入的ThisType中的泛型,提示其下所定义的函数,在函数体中,其调用者的类型是什么

1
2
3
4
5
6
eg:
const foo = {
bar() {
console.log(this.a); // error,在foo中只有bar一个函数,不存在a
}
}

以上代码块,this.a会出现错误提示,因为在foo中,只有bar函数,不存在a

1
2
3
4
5
6
const foo: { bar: any } & ThisType<{ a: number }> = {
bar() {
console.log(this.bar) // error,因为没有在ThisType中定义
console.log(this.a); // ok
}
}

以上代码,通过交叉类型ThisType传入的泛型,在下面的代码中,可以访问this.a不会报错,虽然this.bar存在,但是没有在ThisType定义,会出现错误提示

in、keyof

in可以理解为对象遍历时候声明的key,类似jsconst key in data中的in
keyof遍历当前类型

1
2
3
4
eg:
type Partial<T> = {
[P in keyof T]?: T[P]; // P will be each key of T
}

整句表示为,遍历泛型T,其中Pkey,通过?转换为可选参数然后返回

never

表示永远不会到达的类型,never 返回类型的函数永不返回,意味着它会抛出一个错误,或者根本不会完成运行。或者不可能有该类型的变量
比如说无限循环,就会被推断为 never 类型

Partial

可以把必选的参数转换为可选参数

1
2
3
4
5
eg:
interface People {
age: number;
name: string;
}

有以上接口,通过Partial操作之后,以下的返回的新的AnonymousPeople类型,就不再是必选的了

1
2
3
4
5
type AnonymousPeople = Partial<People>;
const tom:AnonymousPeople = {
name: 'Tom'
};

Required

与 Partial 相反,把可选参数转换为必选参数

1
2
3
4
interface Foo {
name: string
age?: number
}

以上接口age为非必选,通过Required操作之后,Foo中的每一项都会被转换为必选项

1
2
3
4
5
6
type Bar = Required<Foo>
// 相当于
type Bar = {
name: string
age: string
}

Pick

Pick,顾名思义,就是挑选。

如下接口

1
2
3
4
5
interface Foo {
name: string
age?: number
gender: string
}

当我们通过Pick操作之后,相当于从Foo中,将agegender挑选了出来

1
2
3
4
5
6
type Bar = Pick<Foo, 'age' | 'gender'>
// 相当于
type Bar = {
age?: string
gender: string
}

Omit

Pick相反,Pick是挑选出来,Omit是删除

1
2
3
4
5
type  UserProps =  {
name?:string;
age?:number;
sex?:string;
}

如上接口,通过Omit操作之后

1
2
3
4
5
6
7
// 但是我不希望有sex这个属性我就可以这么写
type NewUserProps = Omit<UserProps,'sex'>
// 等价于
type NewUserProps = {
name?:string;
age?:number;
}

Record

将 K 中的每个属性([P in K]),都转为T类型.
将第一个参数,遍历转换为第二个类型

1
2
3
4
5
6
type petsGroup = 'dog' | 'cat' | 'fish';
interface IPetInfo {
name:string,
age:number,
}

通过Record操作,将petsGroup中的每一项,都转换为了IPetInfo类型

1
2
3
4
5
6
7
type C = Record<petsGroup,IPetInfo>
// 相当于
type C {
dog:IPetInfo
cat:IPetInfo
fish:IPetInfo
}

Exclude

顾名思义,排除的意思,相当于两个类型的差集

1
2
type A = number | string | boolean
type B = number | boolean

如上两个类型,如果通过Exclude操作之后,删除掉了AB共同的部分

1
2
3
type Foo = Exclude<A, B>
// 此时的Foo
type Foo = string

Extract

Exclude 相反,找到 两个类型 的交集

1
2
type A = number | string | boolean
type B = number | boolean

通过Extract操作之后

1
2
3
type Foo = Extract<A, B>
// 此时的Foo
type Foo = number | boolean

unknown

有时候,我们一般不会通过any来进行未知类型的替代,我们可以采用unknown.
ts中,any是顶级类型,对值操作时候不会做任何检测
unknown也是顶级类型,但是不允许随便操作,需要使用 typeofinstanceof 来缩小控制范围

1
2
3
4
function stringifyForLogging(value: unknown): string {
// 缩小范围
if (typeof value === "function") {
}

联合类型中是会被推断为unknown,除非anyunknown 类型会吸收任何类型

1
2
type UnionType1 = unknown | null;       // unknown
type UnionType5 = unknown | any; // any

在交叉类型中,任何类型都可以吸收 unknown 类型

1
2
type IntersectionType1 = unknown & null;       // null
type IntersectionType5 = unknown & any; // any

关于unknown,需要注意:

  • unknown 类型的变量只能赋值给 any 和 unknown
  • 操作 unknown 类型只能通过判断缩小范围