ts类型挑战【七】

103 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情

题目十:includes

// template.ts
type Includes<T extends readonly any[], U> = any;
// test-cases.ts
import { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'>, true>>,
  Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'>, false>>,
  Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 7>, true>>,
  Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 4>, false>>,
  Expect<Equal<Includes<[1, 2, 3], 2>, true>>,
  Expect<Equal<Includes<[1, 2, 3], 1>, true>>,
  Expect<Equal<Includes<[{}], { a: 'A' }>, false>>,
  Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>,
  Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>,
  Expect<Equal<Includes<[false, 2, 3, 5, 6, 7], false>, true>>,
  Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>,
  Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>,
  Expect<Equal<Includes<[1], 1 | 2>, false>>,
  Expect<Equal<Includes<[1 | 2], 1>, false>>,
  Expect<Equal<Includes<[null], undefined>, false>>,
  Expect<Equal<Includes<[undefined], null>, false>>,
]

本题想实现一个类似 js 数组中 includes 的方法:判断参数2是否存在于参数1中,如果存在则返回 true,否则返回 false

在解决这道题目之前,我们先来实现一个判断是否相等的方法(Equal

Equal

ts 类型中想要判断两个类型是否相等,主要是通过 extends 方式

如果传入任意一个类型,全部都满足:分别 extends 传入的两个类型,同时为 true 或同时为 false,则可以说明传入的这两个类型相等

代码实现:

type Equal<X, Y> = 
	(<T>() => T extends X ? 1 : 2) extends
  (<T>() => T extends Y ? 1 : 2) ? true : false
  • T 代表任意值

Includes

接下来,开始实现 Includes 方法

在判断一个值是否存在于数组中的时候,很明显我们需要对数组进行遍历,然后在每次遍历的过程中对比数组当前值是否和传入的值相等。

  • 数组遍历

我们可以通过解构的方式对数组进行遍历

type Includes<T extends readonly any[], U> = T extends [infer First, ...infer Rest] 
	? true 
	: false

使用 infer 定义类型只能在等号后面进行;

当上述代码的 T 中还有值的话(还能解构出来 First),就会执行 true 分支,否则会执行 false 分支。

执行 false 分支的话,代表无法再从 T 中解构出来值(T 为空),就会自动结束。

那么我们接下来的操作,就是修改 true 分支的内容

  • 判断相等,如果相等则返回 true,否则进行下一轮循环
type Includes<T extends readonly any[], U> = T extends [infer First, ...infer Rest] 
	? Equal<First, U> extends true
		? true
		: Includes<Rest, U>
	: false

如果从 T 中解构出来的 First 和传入的 U 相等,则直接返回 true, 否则用数组剩下的内容进行下一轮循环 Includes<Rest, U>(类似 js 中的 reduce