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 是兼容的

在線演示

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章