Typescript类型体操 - LastIndexOf

题目

中文

实现类型的 Array.lastIndexOf, LastIndexOf<T, U> 接受泛型参数 Array T 和 any U 并返回数组 T 中最后一个 U 的索引

示例:

type Res1 = LastIndexOf<[1, 2, 3, 2, 1], 2>; // 3
type Res2 = LastIndexOf<[0, 0, 0], 2>; // -1

English

Implement the type version of Array.lastIndexOf, LastIndexOf<T, U> takes an Array T, any U and returns the index of the last U in Array T

For example:

type Res1 = LastIndexOf<[1, 2, 3, 2, 1], 2>; // 3
type Res2 = LastIndexOf<[0, 0, 0], 2>; // -1

答案

type LastIndexOf<T extends any[], U> = T extends [...infer L, infer R]
    ? (<F>() => F extends R ? 1 : 0) extends <F>() => F extends U ? 1 : 0
        ? L['length']
        : LastIndexOf<L, U>
    : -1;

此题与5153 - IndexOf 同源, 重点在于比较两个类型是否一致, github 上 #27024 中探讨了这个问题, 核心就是判断两个类型 X Y 是否完全相同

我最开始想到的做法:

type EqualTo<X, Y> = [X] extends [Y] ? ([Y] extends [X] ? true : false) : false;

这种方法显然有大问题, 比如输入 any1, 期望的结果是 false,实际结果确是 true

然后看到的一个比较容易理解的做法:

type EqualTo<X, Y> = [X extends Y ? 1 : 0, Y extends X ? 1 : 0] ? [1, 1]

然后这个写法能处理 99% 的场景了, 但是如果输入的是 anyunknown, 那么实际会返回 true, 与期望的 false 不符, 这是为什么呢? 因为根据 typescript 的官方文档, 任何一个类型都可以赋值给 unknown (包括 any), 而 unknown 只能赋值给它本身和 any 类型的变量, 所以 any extends unknown ? 1 : 0unknown extends any ? 1 : 0 返回结果相同都是 1, 那么自然 [any extends unknown ? 1 : 0, unknown extends any ? 1 : 0] extends [1, 1] 就是 true 了.

最后经过一番搜索发现了下面这种写法:

type EqualTo<X, Y> = (<F>() => F extends X ? 1 : 0) extends <F>() => F extends Y ? 1 : 0

这种写法下 anyunknown 输入的情况的终于也能正常返回期望的 false. 那为什么这样写有用呢? 在 github issue#27024 看到的解释:

AFAIK it relies on conditional types being deferred when T is not known. Assignability of deferred conditional types relies on an internal isTypeIdenticalTo check, which is only true for two conditional types if:

  • Both conditional types have the same constraint
  • The true and false branches of both conditions are the same type

这段话的意思大概是:

这段代码起作用主要依靠 F 类型未知时存在延迟的条件类型(上面的 F extends X ? 1 : 0); 延迟条件类型的 可转让性(Assignability) 依赖于一个内部的 isTypeIdenticalTo 方法进行检查, isTypeIdenticalTo 只在两个条件类型满足下面的条件时才反会 true

  • 两个条件类型都有相同的约束
  • 两个条件类型在 truefalse 分支下都是相同的类型

两个条件类型在 truefalse 分支下都是相同的类型

理解下这句话, 从这句话能看出, type EqualTo<X, Y> = (<F>() => F extends X ? 1 : 0) extends <F>() => F extends Y ? 1 : 0, 只有 X 与 Y 是同类型, 才能判定 F extends X ? 1 : 0 (这就是上面解释中所谓的条件类型) 与 F extends Y ? 1 : 0 是兼容的

在线演示

posted @ 2022-11-12 01:43  Laggage  阅读(80)  评论(0编辑  收藏  举报