🤫TS高阶面试题--如何用类型映射实现四则运算

285 阅读3分钟

这是一道地狱级TS面试题,你准备好了吗?

前言

在TS中,传入一个类型,然后你会得到一个新的类型,就像一个函数一样。这个叫 TS 类型映射也好,TS 类型转换也好,TS 类型函数也好,都是一个意思。

今天分享一个有意思的 TS 类型映射:

type Add<T,R> = ...;

type s1 = Add<1,2>; // 3
type s2 = Add<3,5>; // 8

这是ts类型映射哦,不是js函数

image.png image.png

有一个 Add 类型映射,接收两个数字,然后返回两个数字的和。

:有人可能会问,这样做有什么意义,我想定义一个类型为 3 的 type,直接定义就好了,为什么还要搞一个 Add 呢?

:因为有意思😁!

实现加法函数

TS 中一切都是类型,并没有大小之分,更没有四则运算,做加法就不能像普通编程语言中直接相加。那怎么办?

先问一个问题,什么时候,TS 会在意数量?

:在元组类型的时候。

type t1 = [number];
type t2 = [number, number];

type isEqual = t1 extends t2 ? true : false; // false

两个不同数量的元组,类型不同的,所以 isEqual 的类型是 false

辅助 TS 函数

下面我们实现一个辅助 TS 函数 GenerateTuple,接受一个数字,返回一个相同数量子元素的元组。

type t1 = GenerateTuple<3>; // [any, any, any]
type t2 = GenerateTuple<5>; // [any, any, any, any, any]
image.png image.png 实现: ```typescript type GenerateArray = R['length'] extends N ? R : GenerateArray ``` 通过判断 R 数组的长度是否到达了 N,如果到了就返回 R 数组;如果不够,就继续累加 R 数组的长度。

有了辅助函数,就可以写 Add 函数了,非常简单:

type Add<N1 extends number, N2 extends number> = [...GenerateArray<N1>, ...GenerateArray<N2>]['length']

是不是很简单

加法实现了,减法怎么实现呢🤔

实现减法函数

定义一个函数ReduceNum,同样接受两个数字,返回两个数字之差:

type ReduceNum<N1,N2> = ...;

type r1 = ReduceNum<3,2>; // 1
type r2 = ReduceNum<4,1>; // 3
image.png image.png

还是借助上面的辅助函数,实现:

type ReduceNum2<N1 extends number, N2 extends number, R extends any[] = GenerateArray<N1>> = 
    R extends [...GenerateArray<N2>, ...infer Rest]
    ? Rest['length']
    : never;

是不是简单到不行😃

乘法函数

乘法就是加法的累积,也很好写:

type Multiple<N1 extends number, N2 extends number, T extends any[] = [...GenerateTuple<N1>],R extends any[] = []> = 
  T['length'] extends 0 
  ? R['length']
  : T extends [infer _, ...infer T] 
    ? Multiple<N1,N2,T,[...GenerateTuple<N2>, ...R]>
    :never

测试:

type m1 = Multiple<2,3>; //6
type m2 = Multiple<4,4>; //16
type m3 = Multiple<0,4>; //0

完美符合预期

除法函数

除法也可以转成加减法,即不断地从被除数中拿出除数,直到被除数小于除数:

type Division<N1 extends number, N2 extends number, T extends any[] = [...GenerateTuple<N1>], R extends any[] = []> = 
  T extends [...GenerateTuple<N2>, ...infer T] 
    ?  Division<N1,N2, T, [any, ...R]>
    : [R['length'], T['length']];

测试:

type d1 = Division<4,2>; //[2,0]
type d2 = Division<10,4> //[2,2]
type d4 = Division<5,10> //[0,5]

除法有点特别,结果可以有两个,即商和余数,所以上面就采用了元组的形式,元组的第一位是商,第二位是余数。

结果也完美符合预期


问个问题,你好奇在 TS 中除以 0 会发生什么吗😌?

type d3 = Division<1,0>

答案是,会死循环报错。

总结

TS 函数没什么好讲的,实现代码放出来了,一眼就能看懂,没有什么思想深度,就是有意思而已。

就这样,下课。