詳解TypeScript中的箭頭函數(shù)如何實現(xiàn)重載
這個問題來自于我在網(wǎng)上搜索的時候,基本上清一色的翻譯官網(wǎng)的函數(shù)重載的章節(jié)的內(nèi)容,對于我想要的箭頭函數(shù)的重載沒有太多幫助。包括官網(wǎng),其實也沒有非常明確的說明箭頭函數(shù)該如何重載。
這里先直接上結(jié)論,在 ts 中,可以借助 Call Signatures 這個特性來實現(xiàn)箭頭函數(shù)的重載。原本是用來給函數(shù)聲明增加靜態(tài)屬性的,但是卻可以用來完成箭頭函數(shù)的類型聲明。
type Test = { (s: string): string; (s: number): number; (s: string, b: number): number; }; type getState<T> = { (): T; <K extends keyof T>(key: K): T[K]; };
本質(zhì)就是定一個新的類型,鍵值就用括號包,里面就是不同的入?yún)?,函?shù)的返回值就是鍵值即可。
但是不出意外的話,意外就會發(fā)生,在實操的時候,往往這么寫非常不 ok。
實操
實戰(zhàn)中這么寫,類型聲明是好寫的,但是函數(shù)的實現(xiàn),其實并不好寫,以這樣一個函數(shù)為例:
const deal = (a: number | string, b: number | string): number | string => { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' || typeof b === 'string') { return String(a) + String(b); } return 0; };
當(dāng)入?yún)⒍际?number 時,返回 number,入?yún)⒂幸粋€是 string 時,返回 string。
那么對于這樣一個函數(shù),其實存在幾個重載的情況:
type Deal1 = { (a: number, b: number): number; (a: number, b: string): string; (a: string, b: number): string; (a: string, b: string): string; };
要想把上述類型賦值給 deal 函數(shù),會出現(xiàn)返回值匹配不上的問題。
// ts 會提示類型錯誤: // Type '(a: number | string, b: number | string) => number | string' is not assignable to type 'Deal1'. // Type 'string | number' is not assignable to type 'number'. const deal: Deal1 = ( a: number | string, b: number | string, ): number | string => { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' || typeof b === 'string') { return String(a) + String(b); } ??????? return 0; };
從現(xiàn)象來看,ts 對于這樣的寫法在做檢查的時候,會將當(dāng)前函數(shù)對重載的幾個類型都進行檢查,看看類型上是否能夠賦值,相當(dāng)于:
// 偽代碼,理解意思就行 check1: (a: number, b: number) => number = (a: number | string, b: number | string) => number | string); check2: (a: number, b: string) => string = (a: number | string, b: number | string) => number | string); check3: (a: string, b: number) => string = (a: number | string, b: number | string) => number | string); check4: (a: string, b: string) => string = (a: number | string, b: number | string) => number | string);
對于入?yún)ⅲ捎谑悄孀兾恢?,所?number = number | string 能夠賦值,所以參數(shù)的類型能夠通過校驗,而返回值屬于順變位置,所以 number = number | string 是不能通過類型校驗的。
想要將返回值賦值成功,返回值必須是 number & string 或者 any,前者就是 never 了,此時會發(fā)現(xiàn)雖然賦值通過了 deal 的校驗,但是函數(shù)的實現(xiàn)中,就會報返回值錯誤的問題。如果改成 any,那么雖然不會報錯,但是在函數(shù)中就缺失了對返回值的檢查。如下:
// 返回值改為 number & string, // 賦值處能夠避免類型錯誤 const deal: Deal1 = ( a: number | string, b: number | string, ): number & string => { if (typeof a === 'number' && typeof b === 'number') { // 此時返回值是 never,此處會報類型錯誤 return a + b; } if (typeof a === 'string' || typeof b === 'string') { // 此時返回值是 never,此處會報類型錯誤 return String(a) + String(b); } // 此時返回值是 never,此處會報類型錯誤 return 0; }; // 返回值改為 any, // 賦值處能夠避免類型錯誤 const deal: Deal1 = (a: number | string, b: number | string): any => { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' || typeof b === 'string') { return String(a) + String(b); } // 此處寫任何類型都不會拋錯,缺失了原本的期望的校驗 return undefined; };
那么目前看,想要用這種方式實現(xiàn)箭頭函數(shù)的重載,就只能將返回值設(shè)定為 any,這樣,雖然在用戶使用的時候能夠進行非常好的類型提示,但是開發(fā)者本身不能再借助 ts 完成對這個函數(shù)的返回值的校驗。
此時還有一種寫法,就是 as,寫法如下:
const deal = ((a: string | number, b: number | string): number | string => { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' || typeof b === 'string') { return String(a) + String(b); } return 0; }) as Deal1;
這樣的寫法,即能夠滿足函數(shù)本身的返回值校驗 (可以把 0 改成其他類型試試),同時,又具備了 Deal1 的重載的類型聲明:
// case1: number const case1 = deal(1, 2); // case2: string const case2 = deal('1', 2); // case3: never,入?yún)㈩愋湾e誤 const case3 = deal({}, 2);
最佳實踐
附上完整的代碼:
// 重載類型聲明 type Deal1 = { (a: number, b: number): number; (a: number, b: string): string; (a: string, b: number): string; (a: string, b: string): string; }; const deal = (( // 此時入?yún)?,返回值的類型可自行限? // 無需掛念 Deal1 中的定義 a: string | number, b: number | string, ): number | string => { if (typeof a === 'number' && typeof b === 'number') { return a + b; } if (typeof a === 'string' || typeof b === 'string') { return String(a) + String(b); } // 這樣寫,原函數(shù)具備校驗的能力 return 0; // 通過 as 指定類型 }) as Deal1; const case1 = deal(1, 2); const case2 = deal('1', 2); const case3 = deal({}, 2);
核心就是原函數(shù)類型寫法一致,重載的類型通過 as 進行賦值,這樣就兼顧了類型提示和原函數(shù)的類型校驗。
其他方法
由于 type 和 interface 的用法在此處并無歧義,所以換成 interface 也是 ok。
另一種就是函數(shù)的交叉,也是實現(xiàn)重載的一種方案,如下:
type Deal3 = ((a: number, b: number) => number) & ((a: number, b: string) => string) & ((a: string, b: number) => string) & ((a: string, b: string) => string);
總結(jié)
本文介紹了 TS 實現(xiàn)箭頭函數(shù)重載的幾種方案,借助了 Call Signatures 的特性。
同時給出了這樣寫,在實戰(zhàn)中可能會遇到的類型不匹配的問題及解決方案。希望能夠?qū)τ龅酵瑯訂栴}的同學(xué)有所幫助吧。
到此這篇關(guān)于詳解TypeScript中的箭頭函數(shù)如何實現(xiàn)重載的文章就介紹到這了,更多相關(guān)TypeScript函數(shù)重載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript實現(xiàn)淺拷貝與深拷貝的方法分析
這篇文章主要介紹了JavaScript實現(xiàn)淺拷貝與深拷貝的方法,結(jié)合實例形式總結(jié)分析了JavaScript淺拷貝與深拷貝的定義與使用方法,需要的朋友可以參考下2018-07-07ES6中l(wèi)et、const的區(qū)別及變量的解構(gòu)賦值操作方法實例分析
這篇文章主要介紹了ES6中l(wèi)et、const的區(qū)別及變量的解構(gòu)賦值操作方法,結(jié)合實例形式分析了ES6中l(wèi)et、const的功能、原理、使用方法及數(shù)組、字符串、函數(shù)參數(shù)等解構(gòu)賦值相關(guān)操作技巧,需要的朋友可以參考下2019-10-10用JavaScript實現(xiàn)簡單網(wǎng)頁時鐘
這篇文章主要為大家詳細(xì)介紹了用JavaScript實現(xiàn)簡單網(wǎng)頁時鐘,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-08-08