TypeScript?泛型推斷實(shí)現(xiàn)示例詳解
前言
最近做東西都在用ts,有時(shí)候?qū)懕容^復(fù)雜的功能,如果不熟悉,類型寫起來(lái)還是挺麻煩的。有這樣一個(gè)功能,在這里,我們就不以我們現(xiàn)有的業(yè)務(wù)來(lái)舉例了,我們還是已Animal
舉例,來(lái)說(shuō)明場(chǎng)景。通過一個(gè)工廠來(lái)創(chuàng)建不同的動(dòng)物實(shí)例。在這里我們借助泛型來(lái)實(shí)現(xiàn)類型的約束和動(dòng)態(tài)推到指定類型。
基礎(chǔ)類型準(zhǔn)備
- 用一個(gè)枚舉來(lái)定義
Animal
的類型
enum EAnimalType { dog = 'dog', cat = 'cat', bird = 'bird', }
- 定義不同類型的動(dòng)物有不同的能力類型
type Dog = { /** 大叫 */ shoutLoudly: () => void; } type Cat = { say: () => void; } type Bird = { /** 飛 */ fly: () => void; }
- 定義一個(gè)動(dòng)物的映射類型
type AnimalMap = { [EAnimalType.dog]: Dog; [EAnimalType.cat]: Cat; [EAnimalType.bird]: Bird; }
最終使用的方式
/** * 定義一個(gè)工廠,用來(lái)創(chuàng)建具體動(dòng)物的實(shí)例 * @returns 返回動(dòng)物的實(shí)例 */ function createAnimalFactory<T extends EAnimalType>(): IAnimal<T> { // TODO 根據(jù)業(yè)務(wù)具體實(shí)現(xiàn) return {} as IAnimal<T>; } // 根據(jù)泛型創(chuàng)建狗狗的實(shí)例 const dog = createAnimalFactory<EAnimalType.dog>(); dog.shoutLoudly(); // 根據(jù)泛型創(chuàng)建鳥的實(shí)例 const bird = createAnimalFactory<EAnimalType.bird>(); bird.fly()
基于Interface的實(shí)現(xiàn) (失敗了)
- 接著我們創(chuàng)建一個(gè)
interface
來(lái)定義動(dòng)物基礎(chǔ)接口
export interface IAnimal<T extends EAnimalType> extends IAnimalExtra<T> { id: number; // 編號(hào) name: string; // 名稱 type: T; // 類型 }
我們看到IAnimal
接口繼承了IAnimalExtra
接口,我們想的是通過泛型T
來(lái)動(dòng)態(tài)推導(dǎo)出真實(shí)的類型。讓我們來(lái)看看IAnimalExtra
接口怎么寫
- 寫
IAnimalExtra
接口
export type IAnimalExtra<T extends EAnimalType> { [c in keyof AnimalMap[T]]: AnimalMap[T][c]; }
我們這樣寫,發(fā)現(xiàn)調(diào)試控制臺(tái)報(bào)了很多錯(cuò),具體分析了下錯(cuò)誤,接口不支持這種功能。接著我們嘗試,改成type
試一下。
- 最后用
type
去替代IAnimalExtra
export type IAnimalExtra<T extends EAnimalType> = { [c in keyof AnimalMap[T]]: AnimalMap[T][c]; }
我們用type
,果然不不錯(cuò)了,證明我們的思路是對(duì)的。乍一看,寫的怎么復(fù)雜[c in keyof AnimalMap[T]]: AnimalMap[T][c];
不要怕,我們先具體分析一下這段代碼,就很好理解了。
- 先看
AnimalMap[T]
,可以理解從AnimalMap
類型中獲取對(duì)應(yīng)的類型,近似js中從對(duì)象取值 keyof
接受一個(gè)Object,生成Object的key的字符串的union(聯(lián)合)in
可以遍歷枚舉類型,類似 for...in
整體的功能就是根據(jù)泛型T,獲取AnimalMap
中的某個(gè)類型,遍歷。之后我們專門寫篇文章,介紹下這塊相關(guān)的內(nèi)容。
extends IAnimalExtra<T>
報(bào)錯(cuò)了
在我們最終認(rèn)為可以的情況下,發(fā)現(xiàn)有報(bào)錯(cuò)了,內(nèi)容為【接口只能擴(kuò)展對(duì)象類型或?qū)ο箢愋团c靜態(tài)已知成員的交集】
所有內(nèi)容都基于type 實(shí)現(xiàn)
在我們嘗試了多次之后,發(fā)現(xiàn)Interface
怎么也滿足不了需求,接著我們都換成type去試試。
export type IAnimal<T extends EAnimalType> = IAnimalExtra<T> & { id: number; // 編號(hào) name: string; // 名稱 type: T; // 類型 }
這里我們用了&
交叉類型類合并接口的類型。
換成type之后,已能完全滿足我們的需求,能根據(jù)泛型推斷出我們想要的類型。
完整Demo
/** * 動(dòng)物枚舉 */ export enum EAnimalType { dog = 'dog', cat = 'cat', bird = 'bird', } type Dog = { /** 大叫 */ shoutLoudly: () => void; } type Cat = { say: () => void; } type Bird = { /** 飛 */ fly: () => void; } export type AnimalMap = { [EAnimalType.dog]: Dog; [EAnimalType.cat]: Cat; [EAnimalType.bird]: Bird; } export type IAnimalExtra<T extends EAnimalType> = { [c in keyof AnimalMap[T]]: AnimalMap[T][c]; } export type IAnimal<T extends EAnimalType> = IAnimalExtra<T> & { id: number; // 編號(hào) name: string; // 名稱 type: T; // 類型 } /** * 定義一個(gè)工廠,用來(lái)創(chuàng)建具體動(dòng)物的實(shí)例 * @returns 返回動(dòng)物的實(shí)例 */ function createAnimalFactory<T extends EAnimalType>(): IAnimal<T> { // TODO 根據(jù)業(yè)務(wù)具體實(shí)現(xiàn) return {} as IAnimal<T>; } // 根據(jù)泛型創(chuàng)建狗狗的實(shí)例 const dog = createAnimalFactory<EAnimalType.dog>(); dog.shoutLoudly(); // 根據(jù)泛型創(chuàng)建鳥的實(shí)例 const bird = createAnimalFactory<EAnimalType.bird>(); bird.fly();
結(jié)束語(yǔ)
最近深度使用ts中,有一些感觸,用好類型,前期看著比較費(fèi)時(shí),但隨著項(xiàng)目的迭代,業(yè)務(wù)的復(fù)雜,對(duì)我們后期幫助還是很大的。小伙伴,你們?cè)陧?xiàng)目中用ts了嗎?
以上就是TypeScript 泛型推斷實(shí)現(xiàn)示例詳解的詳細(xì)內(nèi)容,更多關(guān)于TypeScript 泛型推斷的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Js原型鏈constructor prototype __proto__屬性實(shí)例詳解
這篇文章主要介紹了Js原型鏈constructor prototype __proto__屬性實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10前端取消請(qǐng)求及取消重復(fù)請(qǐng)求方式
這篇文章主要為大家介紹了前端取消請(qǐng)求及取消重復(fù)請(qǐng)求方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07深入內(nèi)存原理談JS中變量存儲(chǔ)在堆中還是棧中
JavaScript中基本類型存儲(chǔ)在堆中還是棧中,百度一下有很多不同的答案,本篇文章就來(lái)給大家為此做個(gè)詳細(xì)的介紹,需要的朋友可以參考一下2021-09-09Promise靜態(tài)四兄弟實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了Promise靜態(tài)四兄弟實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07Intersection?Observer交叉觀察器示例解析
這篇文章主要為大家介紹了Intersection?Observer交叉觀察器示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02