实现 Exclude

vortesnailLv.4
2024-09-02 12:00:14148

题目描述

实现内置的工具类型 Exclude<T, U> ,但不能使用它。其作用是从联合类型 T 中排除 U 的类型成员,返回一个新的类型。

例如:

// 得到联合类型 'movies' | 'books'。
type Result1 = MyExclude<'movies' | 'books' | 'games', 'games'> 
// 得到类型 'movies'
type Result2 = MyExclude<'movies' | 'books' | 'games', 'books' | 'games'> 
// 得到联合类型 string | number
type Result3 = MyExclude<string | number | (() => void), Function>

解题思路

根据题目的描述,直观的思路是,遍历 T 中的每一项,分别去检查在不在 U 的范围中,如果在就返回一个 never ,不在就返回当前遍历的项,最终返回新的联合类型。

先举个例子:

type Anis = 'dog' | 'cat'
type Cat = 'cat'

type IsIn = Cat extends Anis ? true : false // type IsIn = true

类似 JavaScript 中的三元表达式,在 TypeScript 中也能使用,上面的例子通过 extends 判断 Cat 是否在 Anis 的约束范围内,在就返回 true ,不在就返回 false

如何遍历 T 的每一项呢?巧妙的是,当条件类型作用于泛型类型时,它们在给定联合类型时变得具有分配性。要理解这句话我们直接看 Exclude 的实现:

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

type Anis = 'dog' | 'cat' | 'pig'
type Anis2 = 'cat' | 'horse'
type Result = MyExclude<Anis, Anis2> // type Result = 'dog' | 'pig'

上面执行 MyExclude<Anis, Anis2> 时等价于:

type Result = ('dog' extends Anis2 ? never : 'dog') |
              ('cat' extends Anis2 ? never : 'cat') |
              ('pig' extends Anis2 ? never : 'pig')

三个条件分支的返回值结合起来就是:

type Result = 'dog' | never | 'pig'

在 TypeScript 中,never 是所有类型的子类型,联合类型中直接可以忽略,所以最终结果就为:

type Result = 'dog' | 'pig'

解题步骤

通过以上的分析,理解了条件类型作用于泛型类型时具有分配性,本题就变得非常简单了。

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

完整代码:

/* _____________ 你的代码 _____________ */

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

/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<MyExclude<'movies' | 'books' | 'games', 'games'>, 'movies' | 'books'>>,
  Expect<Equal<MyExclude<'movies' | 'books' | 'games', 'books' | 'games'>, 'movies'>>,
  Expect<Equal<MyExclude<string | number | (() => void), Function>, string | number>>,
]

总结

本题有一个非常重要的知识点,就是当条件类型作用于泛型类型时,它们在给定联合类型时变得具有分配性。extends 在常规当作约束类型使用时和在泛型中使用时是有区别的。

评论 0
0/1200
表情
排序:
暂无评论,快成为第一个评论者吧~

今天快乐!

去登录开始畅游前端世界

banner

刷题进度

解题排行榜

1

2

3

4

5

6

7

8

9

10