诶,有人会问,这是进阶文章,那有没有基础呢?
很遗憾,暂时没有,需要的话,评论区告诉我😄
内置函数是什么
函数在数学上,就是自变量到因变量的映射。想得到什么样的输出,那给它的输入就是确定的;或者给它什么样输入,他就会有一个固定的输出
而TS内置函数就是一种类型到另一种类型的映射,你也可以理解成类型的转换
OK,我们开始
NonNullable
接收一个联合类型,返回的结果中会去掉Type中的null和undefined类型
下面是使用代码:
type test1 = NonNullable<null | undefined | string | number>;
由函数转换过后的联合类型,只剩下
string | number
下面是手动实现
type MyNonNullable<T> = T extends null | undefined ? never : T;
type test2 = MyNonNullable<null | undefined | string | number>;
效果符合预期
理解:如果泛型T可以转成null | undefined,或者说是null | undefined
的子类,那么就返回never,否则返回T。
关键点是如何识别T是否为
null | undefined
Parameters
接收一个函数类型,返回这个函数的参数数组
下面是使用代码:
type testParam = Parameters<(a: number, b: number) => void>;
得到的类型是一个数组,也可以说是元组。数组里面每一项都和函数的参数的类型一致
下面是手动实现:
type MyParamters<T extends Function> = T extends (...args: infer K) => any ? K : never;
type testParam2 = MyParamters<(a: number, b: number) => void>;
效果符合预期 这里用到了extends来判断T是属于哪种类型的函数,然后用infer来获取参数的类型,或者说用K来代表参数的类型
ConstructorParameters
接收一个构造器类型,返回构造函数中需要的参数类型
下面是使用方法:
interface person {
new (name: string, age: number): otherType; // 返回其他类型
setAge(newAge: number): number;
}
interface Animal {
new (name: string, isBig: boolean): otherType; // 返回其他类型
setName(newName: string): number;
}
type testConstru = ConstructorParameters<person>;
type testConstru = ConstructorParameters<Animal>;
下面是手动实现
type MyConstructorParameters<T extends new (...keys: any) => any> = T extends new (...keys: infer K) => any ? K : never;
解释:
要求传入的是”定义了构造函数的接口“,或者”typeof class“。不按要求传入接口和class,都会报错。
为啥要typeof class,我们知道class既可以当作值来使用,或者是类型来使用。而在typeof 语法中,会将class看作值,而class的本质就是构造函数,那么typeof class就是获取class对应的构造函数的类型。
返回的K就是构造函数的参数
题外Tips:
- 和JS不同,TS中不能由函数来定义构造函数,也就是说,new 一个函数会报错。
为什么会这样,因为在TS中,把函数定义和构造函数的定义完全分开了
- 再说一下class 和 typeof class的区别,这理解构造函数很重要
```typescript
class Person {
constructor(public personName: string, public personAge: string) {}
getName() {
} setName(newName: string) {return this.personName;
} static laugh(){ console.log(‘laugh’); } }this.personName = newName;
const p1 = {} as Person;
const p2 = {} as typeof Person;
> 上面的代码中,p1是获取Person的实例类型,p2是获取Person的构造函数类型
我们来看下面的截图,就能更清楚地理解了:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/27760829/1654877192260-20e738d8-d0b6-4373-8748-025e630a1047.png#clientId=u1bd96d54-00ed-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=172&id=u5c287930&margin=%5Bobject%20Object%5D&name=image.png&originHeight=344&originWidth=932&originalType=binary&ratio=1&rotation=0&showTitle=false&size=39465&status=done&style=none&taskId=u1849903c-2948-4e74-b5ce-63c523d3045&title=&width=466)
> p1所能访问的属性,和实例化class Person后对象的属性一致
![image.png](https://cdn.nlark.com/yuque/0/2022/png/27760829/1654877250150-c4b70a2f-5dae-496b-96f9-f8c986aa1722.png#clientId=u1bd96d54-00ed-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=344&id=ub63f6ecb&margin=%5Bobject%20Object%5D&name=image.png&originHeight=688&originWidth=988&originalType=binary&ratio=1&rotation=0&showTitle=false&size=70650&status=done&style=none&taskId=u102d8e65-8365-4eb6-8894-ca87c4dde23&title=&width=494)
> p2所能访问的属性,有静态属性laugh--构造函数的属性;还有其他的属于Function的属性
<a name="bL2Yo"></a>
## ReturnType<Type>
接收一个函数类型的Type,返回函数的返回类型<br />下面是用法:
```typescript
type testReturn = ReturnType<(a: string) => number>;
下面是手动实现:
type MyReturnType<T extends Function> = T extends (...keys: any[]) => infer R ? R : never;
type testReturn2 = MyReturnType<(a: string) => [number, string]>;
解释:我们用推断类型,来用R表示函数的返回类型;用条件类型来返回R。整体逻辑:如果T是这种函数的子类,就返回它的返回类型R
InstanceType
也没什么神奇的,就是获取构造函数的返回类型
下面是用法:
interface person {
new (personName: string, personAge: number): instancePerson;
}
interface instancePerson{
personName: string;
personAge: number;
setName(newName: string): number;
}
type testInstance = InstanceType<person>;
传入的参数和ConstructorParameters
一致,需要传入一个定义了构造函数的接口,和typeof class
下面是手动实现:
type MyInstanceType<T extends new (...keys: any)=>any > = T extends abstract new (...keys: any) => infer R ? R : never;
解释:
- 官方源码在每个new前面都加了一个 abstract,我试了下,效果都一样,可加可不加
- 整体逻辑,先判断传进来的是否为构造函数类型,然后再获取它的返回类型
- 使用场景:获取VUE组件的实例
ThisParamterType
获取函数的this类型
下面是用法:
function toHex(this: Number) {
return this.toString(16);
}
function numberToString(n: ThisParameterType<typeof toHex>) {
return toHex.apply(n);
}
- 这个例子有点绕
- 先声明一个’数字转16进制‘的函数
toHex
,这个函数需要调用传入参数的toString
属性。在内部的做法是用this.toString
,所以在参数部分就要定义this
的类型—Number。 - 定义
this
的类型,一般是放在形参的第一个位置。而在调用该函数的时候,并不需要传入this
这个实参。 - 内部的
this
由调用该函数的对象决定,或者用apply、bind、call强制绑定。在上面的例子中,就用apply强制绑定了toHex
函数的this为n
- 在
numberToString
的函数参数中,取了toHex
的函数的this
类型。其中函数不能直接作为类型使用,所以需要加typeof
下面是手动实现:
type myThisParamterType<T> = T extends (this: infer F, ...args: any)=>any ? F : never;
type testmyThisParamterType = myThisParamterType<typeof toHex>
解释:
- 可以在传入泛型位置加一个类型的约束,比如
type myThisParamterType<T exnteds (...args: any)=>any>
,约束传进来的函数,不是函数就报错。这个可加可不加,看个人喜好 - 整体逻辑和上面的相似,就不赘述了
总结:
- 这篇文章描述了一些比较难理解的TS内置函数,
- 从‘使用’和’手动实现‘两个角度分别对每个内置函数进行阐述
- 除了获得内置函数的理解之外,还知道了class和typeof class的区别,还知道了this在TS的使用
- 文中还提到了一些条件类型,推断类型的使用,这个在TS进阶的领域是很重要的,我之后会专门写篇文章来从我的角度对这知识进行阐述
- 下篇文章讲最后一个和this有关的内置函数,ThisType
,理解了这个函数,相信在以后的TS编写过程中,会有了更强的类型约束的意识 - 如果有不明白的地方,评论区告诉我,或者加我微信