TypeScript 泛型推断实现示例详解

目录

前言

基础类型准备

最终使用的方式

基于Interface的实现 (失败了)

所有内容都基于type 实现

完整Demo

结束语

前言

最近做东西都在用ts,有时候写比较复杂的功能,如果不熟悉,类型写起来还是挺麻烦的。有这样一个功能,在这里,我们就不以我们现有的业务来举例了,我们还是已Animal举例,来说明场景。通过一个工厂来创建不同的动物实例。在这里我们借助泛型来实现类型的约束和动态推到指定类型。

基础类型准备

用一个枚举来定义Animal的类型

enum EAnimalType { dog = 'dog', cat = 'cat', bird = 'bird', }

定义不同类型的动物有不同的能力类型

type Dog = { /** 大叫 */ shoutLoudly: () => void; } type Cat = { say: () => void; } type Bird = { /** 飞 */ fly: () => void; }

定义一个动物的映射类型

type AnimalMap = { [EAnimalType.dog]: Dog; [EAnimalType.cat]: Cat; [EAnimalType.bird]: Bird; } 最终使用的方式 /** * 定义一个工厂,用来创建具体动物的实例 * @returns 返回动物的实例 */ function createAnimalFactory<T extends EAnimalType>(): IAnimal<T> { // TODO 根据业务具体实现 return {} as IAnimal<T>; } // 根据泛型创建狗狗的实例 const dog = createAnimalFactory<EAnimalType.dog>(); dog.shoutLoudly(); // 根据泛型创建鸟的实例 const bird = createAnimalFactory<EAnimalType.bird>(); bird.fly() 基于Interface的实现 (失败了)

接着我们创建一个interface 来定义动物基础接口

export interface IAnimal<T extends EAnimalType> extends IAnimalExtra<T> { id: number; // 编号 name: string; // 名称 type: T; // 类型 }

我们看到IAnimal接口继承了IAnimalExtra接口,我们想的是通过泛型T来动态推导出真实的类型。让我们来看看IAnimalExtra接口怎么写

IAnimalExtra接口

export type IAnimalExtra<T extends EAnimalType> { [c in keyof AnimalMap[T]]: AnimalMap[T][c]; }

我们这样写,发现调试控制台报了很多错,具体分析了下错误,接口不支持这种功能。接着我们尝试,改成type试一下。

最后用type 去替代 IAnimalExtra

export type IAnimalExtra<T extends EAnimalType> = { [c in keyof AnimalMap[T]]: AnimalMap[T][c]; }

我们用type,果然不不错了,证明我们的思路是对的。乍一看,写的怎么复杂[c in keyof AnimalMap[T]]: AnimalMap[T][c]; 不要怕,我们先具体分析一下这段代码,就很好理解了。

先看AnimalMap[T],可以理解从AnimalMap类型中获取对应的类型,近似js中从对象取值

keyof 接受一个Object,生成Object的key的字符串的union(联合)

in 可以遍历枚举类型,类似 for...in

整体的功能就是根据泛型T,获取AnimalMap中的某个类型,遍历。之后我们专门写篇文章,介绍下这块相关的内容。

extends IAnimalExtra<T> 报错了

在我们最终认为可以的情况下,发现有报错了,内容为【接口只能扩展对象类型或对象类型与静态已知成员的交集】

所有内容都基于type 实现

在我们尝试了多次之后,发现Interface怎么也满足不了需求,接着我们都换成type去试试。

export type IAnimal<T extends EAnimalType> = IAnimalExtra<T> & { id: number; // 编号 name: string; // 名称 type: T; // 类型 }

这里我们用了&交叉类型类合并接口的类型。

换成type之后,已能完全满足我们的需求,能根据泛型推断出我们想要的类型。

完整Demo /** * 动物枚举 */ 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; // 编号 name: string; // 名称 type: T; // 类型 } /** * 定义一个工厂,用来创建具体动物的实例 * @returns 返回动物的实例 */ function createAnimalFactory<T extends EAnimalType>(): IAnimal<T> { // TODO 根据业务具体实现 return {} as IAnimal<T>; } // 根据泛型创建狗狗的实例 const dog = createAnimalFactory<EAnimalType.dog>(); dog.shoutLoudly(); // 根据泛型创建鸟的实例 const bird = createAnimalFactory<EAnimalType.bird>(); bird.fly(); 结束语

最近深度使用ts中,有一些感触,用好类型,前期看着比较费时,但随着项目的迭代,业务的复杂,对我们后期帮助还是很大的。小伙伴,你们在项目中用ts了吗?

以上就是TypeScript 泛型推断实现示例详解的详细内容,更多关于TypeScript 泛型推断的资料请关注易知道(ezd.cc)其它相关文章!

推荐阅读