iT邦幫忙

2024 iThome 鐵人賽

DAY 19
0
JavaScript

TypeScript Type Challenges 冒險篇章:30 天闖關之旅,type 簡單了?你確定?系列 第 19

第19關:Last of Array!TypeScript 吊車尾:漩渦鳴人來也

  • 分享至 

  • xImage
  •  

第19關:Last of Array

關卡簡介

Implement a generic Last<T> that takes an Array T and returns its last element.

實作一個泛型 Last<T>,它接受一個陣列 T,並返回其最後一個元素。

任務說明:

type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]

type tail1 = Last<arr1> // expected to be 'c'
type tail2 = Last<arr2> // expected to be 1

接下來,你的任務是讓下面的type cases測試通過:

type cases = [
  Expect<Equal<Last<[2]>, 2>>,
  Expect<Equal<Last<[3, 2, 1]>, 1>>,
  Expect<Equal<Last<[() => 123, { a: string }]>, { a: string }>>,
]

冒險指南:

從以下幾個方向來思考:

  • 型別推斷 (Type Inference):使用 infer 來從泛型中推導型別。
  • 陣列解構與尾部推斷 (Array Destructuring with Inference):我們的目標是找到陣列的最後一個元素,這可以通過陣列的解構來完成。TypeScript 允許我們使用 ... (Spread Syntax) 把陣列分成前面的部分和最後的元素,然後用 infer 來推斷出最後一個元素的型別。
  • T extends [...any, infer L]:這樣的語法可以分割陣列,把最後一個元素取出來並賦值給 L。這個方法無論陣列長度是固定還是可變,都適用。
  • 逆推元素 (Reverse Tuple Inference):透過這種解構方法,不需要知道陣列的確切長度,就可以推斷出最後一個元素的型別,對於各種長度的陣列都有效。
  • 邊界情況處理 (Edge Case Handling):要考慮空陣列的情況。如果陣列是空的,則應該返回 never 來避免錯誤。

通關方式:

解法1:

type Last<T extends any[]> = T extends [...any, infer L] ? L : never

// 也可以改使用 _ 
type Last<T extends any[]> = T extends [...infer _, infer L] ? L : never

細節分析:

  • 條件型別 (Conditional Types):條件型別允許根據不同的型別來執行不同的邏輯。在這裡,T extends [...any, infer L] 用於檢查 T 是否符合「一個陣列,且最後一個元素可以推斷為 L」的條件。

    • T extends [...any, infer L] 表示 T 是一個包含任意數量元素的陣列,並且最後一個元素會被推斷為 L
    • 如果條件為真,返回 L,否則返回 never
  • 型別推斷 (Type Inference):使用 infer 來從 T 中推斷最後一個元素的型別並將它賦值給 L。這是 TypeScript 在處理陣列或元組中元素時常用的技巧,特別是當我們不知道具體的長度時。

    • infer L 會嘗試推斷出陣列的最後一個元素的型別,這就是我們想要的結果。
  • 省略元素 (Omitting Elements):在 T extends [...infer _, infer L] 中,...infer _ 用來忽略陣列中的其他元素,只關注最後一個元素 L。這表示我們不需要具體知道前面有多少元素,只需要知道最後一個元素是什麼。

    • _ 在 TypeScript 中通常表示「不需要的型別」或「無關緊要的型別」,所以我們使用 ...infer _ 來跳過前面的所有元素。

解法2 (花式):

type Last<T extends any[]> = [never, ...T][T['length']]

細節分析:

  • 型別操作 (Type Manipulation)

    • 這種方法利用了陣列型別的索引特性來獲取最後一個元素。我們將 T 前面加上 never,這樣可以在訪問 T['length'] 時得到陣列的最後一個元素。
    • T['length'] 會返回陣列 T 的長度,而這個長度正好是原始陣列最後一個元素的索引。
  • 步驟細節:

    • 假設 T[string, number, boolean],長度為 3。
    • [never, ...T] 會生成一個新陣列 [never, string, number, boolean],長度變為 4。
    • 當我們訪問 [never, string, number, boolean][3],即 T['length'] 的位置,會得到原始 T 的最後一個元素 boolean
  • never 的作用:

    1. 處理空陣列:假設 T 是一個空陣列 []T['length'] 為 0。此時 [never, ...T] 變成 [never],而 [never][0] 會返回 never。這是一個安全的回應,因為空陣列沒有最後一個元素,因此返回 never 是合理的。

    2. 防止索引越界:對於非空陣列,[never, ...T] 確保訪問 T['length'] 的索引是有效的。例如,對於 T = [string, number]T['length'] 是 2,而 [never, string, number][2] 正好返回 number,也就是 T 的最後一個元素。如果沒有 neverT[T['length']] 可能會導致越界訪問錯誤。

這樣,我們就能順利通過測試啦 🎉 😭 🎉

總結:

本次介紹了 Last of Array 的實作,下一關會挑戰 Pop,期待再相見!


上一篇
第18關:Tuple to Union !TypeScript 易容術 :元組變聯合型別
下一篇
第20關:Pop!TypeScript Popping 舞王:震掉最後一個元素
系列文
TypeScript Type Challenges 冒險篇章:30 天闖關之旅,type 簡單了?你確定?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言