TS类型挑战之Unqiue的实现
前言
该类型挑战题目来源于github上的优秀作品——Type Challenges · GitHub,在此处分享题解以及思路。
题目
/*
5360 - Unique
-------
by Pineapple (@Pineapple0919) #中等 #array
### 题目
实现类型版本的 Lodash.uniq 方法, Unique<T> 接收数组类型 T, 返回去重后的数组类型.
```ts
type Res = Unique<[1, 1, 2, 2, 3, 3]>; // expected to be [1, 2, 3]
type Res1 = Unique<[1, 2, 3, 4, 4, 5, 6, 7]>; // expected to be [1, 2, 3, 4, 5, 6, 7]
type Res2 = Unique<[1, "a", 2, "b", 2, "a"]>; // expected to be [1, "a", 2, "b"]
type Res3 = Unique<[string, number, 1, "a", 1, string, 2, "b", 2, number]>; // expected to be [string, number, 1, "a", 2, "b"]
type Res4 = Unique<[unknown, unknown, any, any, never, never]>; // expected to be [unknown, any, never]
```
> 在 Github 上查看:https://tsch.js.org/5360/zh-CN
*/
/* _____________ 你的代码 _____________ */
type Unique<T> = any;
/* _____________ 测试用例 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<Unique<[1, 1, 2, 2, 3, 3]>, [1, 2, 3]>>,
Expect<Equal<Unique<[1, 2, 3, 4, 4, 5, 6, 7]>, [1, 2, 3, 4, 5, 6, 7]>>,
Expect<Equal<Unique<[1, 'a', 2, 'b', 2, 'a']>, [1, 'a', 2, 'b']>>,
Expect<Equal<Unique<[string, number, 1, 'a', 1, string, 2, 'b', 2, number]>, [string, number, 1, 'a', 2, 'b']>>,
Expect<Equal<Unique<[unknown, unknown, any, any, never, never]>, [unknown, any, never]>>,
]
解题思路
首先实现一个方法,用于判断某个元素是否存在于数组中
在此实现一个Include类型,其接收两个泛型,第二个参数是数组,该类型可用于判断第一个参数是否存在于第二个数组中
type Include<X, Y extends any[]> = Y extends [infer F, ...infer R] ? X extends F ? true : Include<X, R> : false
有了Include方法,就可以去实现Unique了
type Unique<T extends any[]> = T extends [...infer F, infer R] ? Include<R, F> extends true ? Unique<F> : [...Unique<F>, R] : [];通过infer关键字,可以每次取到数组中的最后一个元素(别问为什么不写成[infer R, ...infer F])因为题目的要求是保留第一次出现的元素,那么就是从后比较,通过上述实现的Include方法,取到最后一个元素和前面所有的元素进行比较,如果返回false,则保留当前元素,如果返回true,则返回剩下元素的Unique的结果
写到这里就已经实现了大半了,但是会发现第四第五个用例通过不了,可以做个简单的测试看看是为什么
type test1 = Include<1, [number]>; // true
type test2 = Include<number, [1]>; // false
type test3 = Unique<[string, number, 1, 'a', 1, string, 2, 'b', 2, number]> // 期望 [string, number, 1, 'a', 2, 'b'] 实际 [string, number]
所以回到一开始的Include方法,不可以通过简单的 X extends F来判断两个元素是否相等,所以接下来实现MyEqual类型
// 这是官方的题解,可以自己实现一下
type MyEqual<X, Y> = (<T>() => T extends X ? true : false) extends (<T>() => T extends Y ? true : false) ? true : false;
然后修改Include类型
type Include<X, Y extends any[]> = Y extends [infer F, ...infer R] ? MyEqual<X, F> extends true ? true : Include<X, R> : false
定义MyEqual和Include类型之后,Unique也就通过了所有的测试用例,这一题就结束了