Implement a generic
First<T>that takes an ArrayTand returns its first element's type.
實現一個泛型 First<T>,它接受一個數組 T,並返回其第一個元素的類型。
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
接下來,你的任務是讓下面的type cases測試通過:
type cases = [
  Expect<Equal<First<[3, 2, 1]>, 3>>,
  Expect<Equal<First<[() => 123, { a: string }]>, () => 123>>,
  Expect<Equal<First<[]>, never>>,
  Expect<Equal<First<[undefined]>, undefined>>,
]
在這一關中,我們可以從以下幾個方向來思考:
T 是一個 array ?我們將會用到:
extends 在第三關已經介紹過,可以回到第三關參考喔!
解法1:
type First<T extends any[]> = T extends []? never : T[0]
細節分析:
T extends any[]:此部分確保 T 必須是一個陣列類型,任何陣列都適用,不限定具體類型。T extends []:這裡使用條件類型檢查 T 是否是一個空陣列。如果是空陣列,就返回 never,表示沒有元素。T[0]:如果 T 不是空陣列,則返回陣列的第一個元素類型。T[0] 是 TypeScript 用來取得陣列第一個元素的標準方式。解法2:
type First<T extends any[]> = T['length'] extends 0? never : T[0]
細節分析:
T['length']:T['length'] 取得陣列的長度。這是一種靜態檢查陣列是否為空的方式。這裡我們通過訪問 T 的 length 屬性來獲取數組的長度。TypeScript 的數組類型都有一個隱式的 length 屬性,這表示數組中元素的數量。
T['length'] extends 0:這裡檢查陣列長度是否為 0。如果是,則返回 never,表示空陣列。
T[0]:當陣列不為空時,返回第一個元素的類型。這是用來獲取非空陣列第一個元素的標準方式。
解法3:
type First<T extends any[]> = T extends {length:0} ? never : T[0]
細節分析:
T extends { length: 0 }:使用條件類型來檢查 T 是否是一個長度為 0 的陣列。透過直接檢查 T 是否符合具有 length 屬性且該屬性為 0 的結構。如果長度為 0,返回 never。
T[0]:和之前的解法類似,當陣列不為空時,直接返回第一個元素的類型。
解法4:
type First<T extends any[]> = T extends [infer P, ...any[]] ? P : never
細節分析:
infer P: 使用 infer 關鍵字從元組的第一個元素中提取類型 P。這樣可以靈活地獲取數組中的第一個元素。...any[]: 表示剩餘的元素可以是任何類型,這樣可以匹配任何長度的數組。P:如果 T 是一個包含元素的數組,返回第一個元素的類型;否則返回 never。這樣,我們就能順利通過測試啦 🎉 😭 🎉
infer keyword in TypeScript allows you to declare a type variable within a conditional type. When used in the context of a conditional type, infer tells TypeScript to:
type ElementType<T> = T extends (infer E)[] ? E : never;
infer E tells TypeScript to infer the type of the array element and assign it to the type variable E.T matches the pattern (infer E)[], TypeScript infers the type of E based on the type of elements in the array T.type ArrayType1 = ElementType<number[]>; // ArrayType1 is number
type ArrayType2 = ElementType<string[]>; // ArrayType2 is string
function format(value: string | number) {
if (typeof value === 'string') {
    return value.trim();
} else {
    return value.toFixed(2); // we're sure it's number
}
// not a string or number
// "value" can't occur here, so it's type "never"
}
// Function returning never must not have a reachable end point
function error(message: string): never {
  throw new Error(message);
}
const reportError = function () {
    throw Error('my error');
}
// reportError's type is () => never.
error function has a return type of never, the return type of fail function is inferred to be never as well.fail function itself doesn't explicitly throw an error, it calls error, which is a never returning function:// Inferred return type is never
function fail() {
  return error("Something failed");
}
while (true) {}), which means it never terminates normally.never, indicating that it never produces a normal value. Instead, it continues running indefinitely. // Function returning never must not have a reachable end point
function infiniteLoop(): never {
  while (true) {}
}
const loop = function () {
    while(true) {}
}
// loop's type is () => never.
本次介紹了 First of Array 的實作,下一關會挑戰 Length of Tuple,期待再相見!