TS类型挑战之Unqiue的实现

95 阅读2分钟

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]>; // truetype test2 = Include<number, [1]>; // falsetype 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也就通过了所有的测试用例,这一题就结束了