到精通:TypeScript-New-Handbook 函數(shù)進(jìn)階實(shí)戰(zhàn)教程)
TypeScript 泛型入門(mén)到精通TypeScript-New-Handbook 函數(shù)進(jìn)階實(shí)戰(zhàn)教程【免費(fèi)下載鏈接】TypeScript-New-HandbookIncubation repository for the new TypeScript handbook 項(xiàng)目地址: https://gitcode.com/gh_mirrors/ty/TypeScript-New-HandbookTypeScript 泛型是類(lèi)型系統(tǒng)的靈魂也是從新手走向進(jìn)階的必經(jīng)之路。本文基于微軟新手冊(cè)項(xiàng)目TypeScript-New-Handbook的官方章節(jié)用最通俗的方式帶你完成 TypeScript 泛型入門(mén)到精通的全過(guò)程從零開(kāi)始理解泛型函數(shù)、類(lèi)型推斷、約束條件再到編寫(xiě)高質(zhì)量泛型代碼的實(shí)戰(zhàn)技巧。無(wú)論你是剛接觸 TypeScript 的新手還是想系統(tǒng)補(bǔ)全泛型知識(shí)的開(kāi)發(fā)者這份 TypeScript 泛型教程都能幫你建立完整、正確的知識(shí)框架。什么是 TypeScript 泛型為什么你需要它泛型Generics的作用是描述兩個(gè)值之間的類(lèi)型對(duì)應(yīng)關(guān)系。想象一個(gè)場(chǎng)景你寫(xiě)了一個(gè)函數(shù)想返回?cái)?shù)組的第一個(gè)元素。不用泛型時(shí)返回值只能是any類(lèi)型信息全部丟失function firstElement(arr: any[]) { return arr[0]; }而使用泛型后函數(shù)像搭橋一樣把輸入和輸出的類(lèi)型關(guān)聯(lián)起來(lái)function firstElementT(arr: T[]): T { return arr[0]; } const s firstElement([a, b, c]); // s 的類(lèi)型是 string const n firstElement([1, 2, 3]); // n 的類(lèi)型是 number這就是 TypeScript 泛型最核心的價(jià)值一次編寫(xiě)處處保留類(lèi)型安全。這部分內(nèi)容源自官方手冊(cè)的 More on Functions.md 章節(jié)是 TypeScript 泛型函數(shù)教學(xué)的標(biāo)準(zhǔn)起點(diǎn)。TypeScript 泛型函數(shù)類(lèi)型推斷機(jī)制詳解很多初學(xué)者擔(dān)心寫(xiě)泛型要手動(dòng)聲明一大堆類(lèi)型參數(shù)其實(shí)完全不用。TypeScript 會(huì)根據(jù)你傳入的參數(shù)**自動(dòng)推斷Inference**泛型類(lèi)型。比如實(shí)現(xiàn)一個(gè)自己的map函數(shù)兩個(gè)類(lèi)型參數(shù)E和O都可以被自動(dòng)推導(dǎo)出來(lái)function mapE, O(arr: E[], func: (arg: E) O): O[] { return arr.map(func); } const parsed map([1, 2, 3], n parseInt(n)); // number[]這里E從字符串?dāng)?shù)組推斷為stringO從回調(diào)返回值推斷為number。理解TypeScript 泛型類(lèi)型推斷是入門(mén)的關(guān)鍵一步多寫(xiě)幾次這樣的函數(shù)你就能自然掌握它。泛型約束 extends 用法限制類(lèi)型的正確姿勢(shì)有時(shí)我們不能讓泛型接受任意類(lèi)型而需要限定它必須擁有某些能力。這時(shí)用extends關(guān)鍵字添加約束Constraints。下面的函數(shù)要返回兩個(gè)值中較長(zhǎng)的一個(gè)因此要求傳入的值必須有l(wèi)ength屬性function longestT extends { length: number }(a: T, b: T) { if (a.length b.length) { return a; } return b; } const longerArray longest([1, 2], [1, 2, 3]); // number[] const longerString longest(alice, bob); // string const notOK longest(10, 100); // ? 數(shù)字沒(méi)有 length 屬性泛型約束 extends 用法的核心要點(diǎn)約束讓泛型只對(duì)特定類(lèi)型子集開(kāi)放同時(shí)讓類(lèi)型檢查在編譯期就攔住錯(cuò)誤調(diào)用。約束值的常見(jiàn)陷阱注意約束只保證類(lèi)型兼容不保證是同一個(gè)對(duì)象。下面的代碼能通過(guò)編譯但運(yùn)行時(shí)會(huì)出問(wèn)題function minimumLengthT extends { length: number }(obj: T, minimum: number): T { if (obj.length minimum) { return obj; } else { return { length: minimum }; // ? 返回了普通對(duì)象丟失了 T 的其它方法 } }函數(shù)承諾返回與傳入相同的類(lèi)型T實(shí)際卻可能返回一個(gè)只有l(wèi)ength的普通對(duì)象調(diào)用方調(diào)用slice等方法時(shí)就會(huì)崩潰。這是使用泛型約束時(shí)最經(jīng)典的坑官方在 More on Functions.md 中有專(zhuān)門(mén)講解。編寫(xiě)高質(zhì)量 TypeScript 泛型函數(shù)的三大黃金法則寫(xiě)泛型容易上癮一不小心就堆出過(guò)度設(shè)計(jì)的代碼。官方手冊(cè)總結(jié)了三條實(shí)用規(guī)則照著做就能寫(xiě)出高質(zhì)量的泛型函數(shù)盡可能用類(lèi)型參數(shù)本身而不是約束它兩個(gè)看似相同的函數(shù)類(lèi)型推斷結(jié)果天差地別function firstElement1T(arr: T[]) { return arr[0]; } // 返回 T ? function firstElement2T extends any[](arr: T) { return arr[0]; } // 返回 any ?盡量少用類(lèi)型參數(shù)如果一個(gè)類(lèi)型參數(shù)沒(méi)有關(guān)聯(lián)兩個(gè)值它就是多余的。F在這里毫無(wú)意義只會(huì)讓調(diào)用者困惑// ? F 沒(méi)有關(guān)聯(lián)任何值 function filter2T, F extends (arg: T) boolean(arr: T[], func: F): T[]; // ? 直接內(nèi)聯(lián)即可 function filter1T(arr: T[], func: (arg: T) boolean): T[];類(lèi)型參數(shù)至少出現(xiàn)兩次如果某個(gè)類(lèi)型參數(shù)在函數(shù)簽名里只出現(xiàn)一次說(shuō)明它沒(méi)在關(guān)聯(lián)任何東西通??梢詣h掉// ? S 只出現(xiàn)一次 function greetS extends string(s: S) { console.log(Hello, s); } // ? 直接用 string function greet(s: string) { console.log(Hello, s); }記住泛型的本質(zhì)是關(guān)聯(lián)多個(gè)值的類(lèi)型。這條法則貫穿 TypeScript 泛型入門(mén)到精通的始終。泛型對(duì)象類(lèi)型與泛型類(lèi)從 Box 到 Array泛型不止用于函數(shù)。接口、類(lèi)型別名和類(lèi)都可以是泛型的最常見(jiàn)的例子就是ArrayT、MapK, V、SetT和PromiseT。以經(jīng)典的Box容器為例泛型讓它一勞永逸interface BoxT { contents: T; } let box: Boxstring; // 等價(jià)于 { contents: string } type AppleBox BoxApple;泛型類(lèi)也是一樣實(shí)例化時(shí)類(lèi)型參數(shù)自動(dòng)推斷class BoxT { contents: T; constructor(value: T) { this.contents value; } } const b new Box(hello!); // Boxstring?? 注意泛型類(lèi)的靜態(tài)成員不能引用類(lèi)自身的類(lèi)型參數(shù)。因?yàn)轭?lèi)型在運(yùn)行時(shí)會(huì)被完全擦除內(nèi)存里只有一個(gè)Box.defaultValue如果允許Boxstring.defaultValue和Boxnumber.defaultValue各設(shè)各的就會(huì)互相覆蓋。詳細(xì)內(nèi)容見(jiàn) Object Types.md 和 Classes.md。進(jìn)階實(shí)戰(zhàn)條件類(lèi)型與 infer 推導(dǎo)當(dāng)泛型遇到條件類(lèi)型Conditional Types類(lèi)型系統(tǒng)開(kāi)始展現(xiàn)真正的威力。借助infer關(guān)鍵字你可以在條件分支中提取出類(lèi)型type GetReturnTypeT T extends (...args: never[]) infer U ? U : never; type Foo GetReturnType() number; // number type Bar GetReturnType(x: string) string; // string再看一個(gè)自動(dòng)剝開(kāi)數(shù)組類(lèi)型的例子type FlattenT T extends any[] ? T[number] : T; type Str Flattenstring[]; // string type Num Flattennumber; // number條件類(lèi)型還能替代繁瑣的函數(shù)重載。比如根據(jù)入?yún)㈩?lèi)型返回不同對(duì)象一個(gè)泛型函數(shù)即可搞定type NameOrIdT extends number | string T extends number ? IdLabel : NameLabel; function createLabelT extends number | string(idOrName: T): NameOrIdT { throw unimplemented; }這部分是 Types from Transformation.md 的核心內(nèi)容屬于泛型的進(jìn)階高階用法建議在掌握基礎(chǔ)后再深入。泛型函數(shù)重載與可選參數(shù)實(shí)戰(zhàn)提醒學(xué)習(xí)函數(shù)進(jìn)階時(shí)還有兩個(gè)和泛型同樣重要的知識(shí)點(diǎn)需要留意可選參數(shù)標(biāo)記為?的參數(shù)實(shí)際類(lèi)型是number | undefined。給回調(diào)函數(shù)寫(xiě)可選參數(shù)時(shí)要格外小心——它表示回調(diào)可能只帶一個(gè)參數(shù)被調(diào)用而不是你可以傳兩個(gè)參數(shù)。函數(shù)重載重載簽名之外實(shí)現(xiàn)簽名對(duì)外部不可見(jiàn)。能用聯(lián)合類(lèi)型參數(shù)如string | any[]時(shí)優(yōu)先用聯(lián)合類(lèi)型而非重載代碼更簡(jiǎn)單、調(diào)用更靈活。這兩個(gè)主題與泛型函數(shù)一起構(gòu)成了 TypeScript 函數(shù)類(lèi)型體系的完整拼圖全部收錄在 More on Functions.md 中??偨Y(jié)TypeScript 泛型學(xué)習(xí)路徑TypeScript 泛型入門(mén)到精通并不神秘按這條路徑學(xué)習(xí)最有效率掌握泛型函數(shù)類(lèi)型參數(shù)T、自動(dòng)推斷More on Functions.md學(xué)會(huì)泛型約束extends理解約束與同一對(duì)象的區(qū)別遵守三條黃金法則寫(xiě)出高質(zhì)量泛型代碼熟悉泛型對(duì)象類(lèi)型與泛型類(lèi)Object Types.md、Classes.md挑戰(zhàn)條件類(lèi)型與 infer解鎖類(lèi)型編程的高級(jí)玩法Types from Transformation.md如果你想完整閱讀這份官方手冊(cè)的全部章節(jié)可以直接查看項(xiàng)目源碼所有章節(jié)都是清晰的 Markdown 文件非常適合自學(xué)和查閱。希望這篇 TypeScript 泛型實(shí)戰(zhàn)教程能幫你掃清進(jìn)階路上的障礙寫(xiě)出更健壯、更優(yōu)雅的類(lèi)型安全代碼【免費(fèi)下載鏈接】TypeScript-New-HandbookIncubation repository for the new TypeScript handbook 項(xiàng)目地址: https://gitcode.com/gh_mirrors/ty/TypeScript-New-Handbook創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考