「TS类型体操00043」实现 Exclude

211 阅读2分钟

题目

实现一个类型工具 MyExclude<T,U>,从联合类型T中排除U的类型成员,构造出一个新的类型。

例如:

// 把类型 'a' 从 'a'|'b'|'c' 中剔除,得到联合类型 'b'|'c'。
type t1 = MyExclude<'a' | 'b' | 'c', 'a'> 
// 得到类型 'c'
type t2 = MyExclude<'a' | 'b' | 'c', 'a' | 'b'>
// 得到联合类型 string | number
type t3 = MyExclude<string | number | (() => void), Function>

原题链接

实现 Exclude

思路

类比 JavaScript

类比 JavaScript,就是实现一个函数:

这个函数接收 2数组,一个是源数组,一个是剔除数组,将剔除数组中的所有元素,从源数组中剔除掉,返回剔除元素后的源数组

function MyExclude(sources, excludes) {
    const rest = [];
    sources.forEach(s => {
        if (!excludes.includes(s)) {
            rest.push(s);
        }
    });
    return rest;
}

提取逻辑点

1. 遍历源数组,筛选出不在`剔除数组`中的元素,组成一个新数组返回

再把逻辑点翻译成 TypeScript 即可。

翻译成 TypeScript

用 typescript 实现上述逻辑点如下。

遍历源数组,筛选出不在剔除数组中的元素,组成一个新数组返回

创建1 个新的联合类型R,通过 T extends U 遍历 T 这个 联合类型,遍历到的每个类型都在 U 中检查一次,如果找得到就不要(返回never),如果找不到才要(返回T,也就是放入R中),最终返回 R

type Exclude<T,U> = T extends U ? never : T;

extends 遍历+对比的过程:

T: a | b | c
U: a

1. 用 T 的 a, b, c 依次对比 U 的 a
   a,a ==> a可以赋值给a,得到 never(空值,不放R中)
   b,a ==> b不可以赋值给a,得到 T, 也就是把 b 放入 R
   c,a ==> c不可以赋值给a,得到 T,也就是把 c 放入 R

2. 返回 R, 结果是 b | c

实现

综上所述,最终的类型工具 MyExclude 实现为:

type MyExclude<T,U> = T extends U ? never : T;