TS 通过类型也能实现 Array.includes?

题目

在类型系统里实现 JavaScript 的 Array.includes 方法,这个类型接受两个参数,返回的类型要么是 true 要么是 false

例如:

ts 复制代码
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> 
// expected to be `false`

测试 Case:

ts 复制代码
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>>,
]

初始代码:

ts 复制代码
type Includes<T extends readonly any[], U> = any

题解

我第一反应想到的,可以通过条件类型来判断 U 是否可以赋值给 T 中的元素。通过 T[number] 来获取 T 中所有元素的联合类型:

ts 复制代码
type Includes<T extends readonly any[], U> = U extends T[number] ? true : false;

不过这样错了,比如下面的例子:

ts 复制代码
Expect<Equal<Includes<[{}], { a: 'A' }>, false>>,

虽然 { a: 'A' } 可以赋值给 {},但是不代表两者相等。

现在,我们需要一个方案来判断两个类型是否相等,虽然这有点难,不过早已经有前辈写好了:

ts 复制代码
export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
  ? true
  : false

原理有点复杂,准备后面单独写一篇文章来介绍。总计记住就好了。

接下来就可以实现 Includes

ts 复制代码
type Includes<T extends readonly unknown[], U> = T extends [infer First, ...infer Rest]
  ? Equal<First, U> extends true ? true : Includes<Rest, U>
  : false;

我们先判断第一个元素是否为查找类型,然后再判断剩余的数组中是否包含该类型,递归解决问题。

这里面涉及的知识点前面有讲到过,这里再复习一下:

  • T extends readonly unknown[] 类型约束, T 被限制为一个只读数组或元组,其元素类型是 unknown
  • T extends [infer First, ...infer Rest]:这是一个条件类型,它检查 T 是否可以赋值给一个元组类型,其中第一个元素被推断为 First,剩余的元素被推断为 Rest。如果 T 是空数组或空元组,这个条件将不成立。
  • infer First...infer Restinfer 关键字在这里用来在条件类型的上下文中推断元组的第一个元素的类型(First)和剩余元素的数组类型(Rest
  • Equal<First, U> extends true ? true : Includes<Rest, U>:如果 T 可以赋值给 [infer First, ...infer Rest],则进一步检查 First 元素是否与类型 U 相等。这通过另一个假设存在的类型别名 Equal 来完成,它应该能够检查两个类型是否相等。如果 FirstU 相等,结果是 true。如果不相等,递归地调用 Includes<Rest, U> 来检查剩余的元素。
  • : false:如果 T 不是一个非空元组或数组,或者 FirstU 不相等且 Rest 为空数组,则 Includes 类型解析为 false

这样,我们就可以通过 Includes 来判断一个类型是否包含在某个元祖类型中了。

相关推荐
by__csdn20 分钟前
Vue3 setup()函数终极攻略:从入门到精通
开发语言·前端·javascript·vue.js·性能优化·typescript·ecmascript
天天扭码35 分钟前
前端如何实现RAG?一文带你速通,使用RAG实现长期记忆
前端·node.js·ai编程
Luna-player1 小时前
在前端中,<a> 标签的 href=“javascript:;“ 这个是什么意思
开发语言·前端·javascript
lionliu05191 小时前
js的扩展运算符的理解
前端·javascript·vue.js
小草cys1 小时前
项目7-七彩天气app任务7.4.2“关于”弹窗
开发语言·前端·javascript
奇舞精选1 小时前
GELab-Zero 技术解析:当豆包联手中兴,开源界如何守住端侧 AI 的“最后防线”?
前端·aigc
奇舞精选1 小时前
Vercel AI SDK:构建现代 Web AI 应用指南
前端·aigc
神仙别闹2 小时前
基于C语言实现B树存储的图书管理系统
c语言·前端·b树
玄魂3 小时前
如何查看、生成 github 开源项目star 图表
前端·开源·echarts
前端一小卒3 小时前
一个看似“送分”的需求为何翻车?——前端状态机实战指南
前端·javascript·面试