這此的挑戰主要是創建一個 Reverse,用於反轉給定字串中的字元。例如將 Revers<‘rehsaD’>轉換成”Dasher”
infer
先來了解一下 infer 的作用,infer 可以在 extends 中的條件語句中推斷待判斷的類型。
它的目的在於創造分支,增加了型別的表達能力。這樣寫的時候,當 T
能指派給 U
,則該型別為 X
,反之則型別為 Y
。
我們可以將它比喻為一種“推斷”機制。當你使用 infer
時,你其實是在告訴 TypeScript:「請你在這個情境下幫我推斷出一個型別」。
一個簡單理解的例子,在這個範例下,還不會 infer 的情況會這樣寫
type Ids = number[];
type Names = string[];
type Unpacked<T> = T extends Names ? string : T extends Ids ? number : T;
type idType = Unpacked<Ids>; // idType 為 number
type nameType = Unpacked<Names>; // nameType 為string
用 infer 的話會這樣寫
type Unpacked<T> = T extends (infer R)[] ? R : T;
type idType = Unpacked<Ids>; // idType 為 number
type nameType = Unpacked<Names>; // nameType 為string
其他範例
範例 1:從 Promise 中推斷結果型別
在這個範例中,我們會建立一個型別幫助器(Type Helper),它可以從一個 Promise 中推斷出其解析(resolve)的值的型別。
type PromiseType<T> = T extends Promise<infer R> ? R : T;
// 使用範例
async function getStringAsync(): Promise<string> {
return 'hello';
}
type ResultType = PromiseType<ReturnType<typeof getStringAsync>>;
// ResultType 被推斷為 string
範例 2:從函數型別中推斷參數型別
type FirstParameter<T> = T extends (infer P, ...args: any[]) => void ? P : never;
// 使用範例
function exampleFunction(x: number, y: string): any {
// ...
}
type ExampleParam = FirstParameter<typeof exampleFunction>;
// ExampleParam 被推斷為 number
FirstParameter<T>
用於獲取函數的第一個參數的型別。這裡使用 infer
來捕獲第一個參數的型別。
範例 3:從陣列中推斷元素型別
type ElementType<T> = T extends (infer U)[] ? U : never;
// 使用範例
type ArrayType = ElementType<T[]>;
// ArrayType 被推斷為 string
在這裡,ElementType<T>
檢查 T
是否是一個陣列。如果是,它使用 infer
來推斷出陣列裡元素的型別。
這些範例展示了 infer
在各種不同情境下的用法,從基本的型別推斷到更複雜的函數和陣列操作。這些範例應該有助於你理解 infer
如何在實際程式碼中運用。
解題
從字串的開頭剝離每個字元,並將其添加到新字串的末尾,遞歸地執行此操作,直到原始字串為空。結果是原始字串的反轉版本。
type Reverse<Str extends string> = Str extends `${infer FirstChar}${infer Rest}`
? `${Reverse<Rest>}${FirstChar}`
: Str;
假設字串Str
為 “abcde” ,會先判斷 ‘a’是否符合 Str,因此FirstChar
推斷為 a,Rest
推斷為 bcde,執行${Reverse<Rest>}${FirstChar}
。判斷’b’是否符合 Str …
-
首次匹配:
FirstChar
推斷為"a"
(字符串的第一個字符)。Rest
推斷為"bcde"
(剩餘的字符)。- 因此,
Reverse<"abcde">
變成${Reverse<"bcde">}"a"
。
-
對
"bcde"
進行遞歸:- 對
"bcde"
使用相同的過程:FirstChar
是"b"
,Rest
是"cde"
。Reverse<"bcde">
變成${Reverse<"cde">}"b"
。
- 現在,原始表達式變為
${Reverse<"cde">}"b""a"
。
- 對
-
繼續遞歸:
- 對
"cde"
、"de"
和"e"
重複相同的過程。 - 每一步都把當前的
FirstChar
移到最後並對Rest
進行遞歸。
- 對
-
遞歸終止:
- 當到達單個字符
"e"
時,遞歸終止,因為這時Str
只包含一個字符,所以直接返回它自己。 - 因此,
Reverse<"e">
為"e"
。
- 當到達單個字符
-
組合結果:
- 逐步組合回溯的結果,最終得到
"edcba"
。
- 逐步組合回溯的結果,最終得到
過程示例
整個過程展開來看是這樣的:
Reverse<"abcde">
-> `${Reverse<"bcde">}"a"`
-> `${`${Reverse<"cde">}"b"`}"a"`
-> `${`${`${Reverse<"de">}"c"`}"b"`}"a"`
-> `${`${`${`${Reverse<"e">}"d"`}"c"`}"b"`}"a"`
-> `${`${`${`"e"`"d"`}"c"`}"b"`}"a"`
-> "edcba"