手动实现TS内置函数-进阶

457 阅读5分钟

诶,有人会问,这是进阶文章,那有没有基础呢?

很遗憾,暂时没有,需要的话,评论区告诉我😄

内置函数是什么

函数在数学上,就是自变量到因变量的映射。想得到什么样的输出,那给它的输入就是确定的;或者给它什么样输入,他就会有一个固定的输出

而TS内置函数就是一种类型到另一种类型的映射,你也可以理解成类型的转换

OK,我们开始

NonNullable<Type>

接收一个联合类型,返回的结果中会去掉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>

接收一个函数类型,返回这个函数的参数数组

下面是使用代码:

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<Type>

接收一个构造器类型,返回构造函数中需要的参数类型

下面是使用方法:

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;

解释:

  1. 要求传入的是”定义了构造函数的接口“,或者”typeof class“。不按要求传入接口和class,都会报错。

为啥要typeof class,我们知道class既可以当作值来使用,或者是类型来使用。而在typeof 语法中,会将class看作值,而class的本质就是构造函数,那么typeof class就是获取class对应的构造函数的类型。

  1. 返回的K就是构造函数的参数

题外Tips:

  1. 和JS不同,TS中不能由函数来定义构造函数,也就是说,new 一个函数会报错。

为什么会这样

因为在TS中,把函数定义和构造函数的定义完全分开了

  1. 再说一下class 和 typeof class的区别,这理解构造函数很重要
class Person {
	constructor(public personName: string, public personAge: string) {}
	getName() {
		return this.personName;
	}
	setName(newName: string) {
		this.personName = newName;
	}
  static laugh(){
    console.log('laugh');
  }
}

const p1 = {} as Person;

const p2 = {} as typeof Person;

上面的代码中,p1是获取Person的实例类型,p2是获取Person的构造函数类型

我们来看下面的截图,就能更清楚地理解了:

p1所能访问的属性,和实例化class Person后对象的属性一致

p2所能访问的属性,有静态属性laugh--构造函数的属性;还有其他的属于Function的属性

ReturnType<Type>

接收一个函数类型的Type,返回函数的返回类型

下面是用法:

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;

解释:

  1. 官方源码在每个new前面都加了一个 abstract,我试了下,效果都一样,可加可不加
  2. 整体逻辑,先判断传进来的是否为构造函数类型,然后再获取它的返回类型
  3. 使用场景:获取VUE组件的实例

ThisParamterType<Type>

获取函数的this类型

下面是用法:

function toHex(this: Number) {
  return this.toString(16);
}
 
function numberToString(n: ThisParameterType<typeof toHex>) {
  return toHex.apply(n);
}
  1. 这个例子有点绕
  2. 先声明一个’数字转16进制‘的函数toHex,这个函数需要调用传入参数的toString属性。在内部的做法是用this.toString,所以在参数部分就要定义this的类型--Number。
  3. 定义this的类型,一般是放在形参的第一个位置。而在调用该函数的时候,并不需要传入this这个实参。
  4. 内部的this由调用该函数的对象决定,或者用apply、bind、call强制绑定。在上面的例子中,就用apply强制绑定了toHex函数的this为n
  5. numberToString的函数参数中,取了toHex的函数的this类型。其中函数不能直接作为类型使用,所以需要加typeof

下面是手动实现:

type myThisParamterType<T> = T extends (this: infer F, ...args: any)=>any ? F : never;

type testmyThisParamterType = myThisParamterType<typeof toHex>

解释:

  1. 可以在传入泛型位置加一个类型的约束,比如 type myThisParamterType<T exnteds (...args: any)=>any>,约束传进来的函数,不是函数就报错。这个可加可不加,看个人喜好
  2. 整体逻辑和上面的相似,就不赘述了

总结:

  1. 这篇文章描述了一些比较难理解的TS内置函数,
  2. 从‘使用’和’手动实现‘两个角度分别对每个内置函数进行阐述
  3. 除了获得内置函数的理解之外,还知道了class和typeof class的区别,还知道了this在TS的使用
  4. 文中还提到了一些条件类型,推断类型的使用,这个在TS进阶的领域是很重要的,我之后会专门写篇文章来从我的角度对这知识进行阐述
  5. 下篇文章讲最后一个和this有关的内置函数,ThisType,理解了这个函数,相信在以后的TS编写过程中,会有了更强的类型约束的意识
  6. 如果有不明白的地方,评论区告诉我,或者加我微信