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 泛型推断的资料请关注我们其它相关文章!

(0)

相关推荐

  • TypeScript中的接口和泛型你了解吗

    目录 接口 对象类型 索引类型 函数类型 继承 交叉类型 接口实现 interface和type的区别 字面量赋值 枚举类型 泛型 泛型函数 泛型接口 泛型类 类型约束 总结 接口 使用 interface 关键字来定义数据类型 对象类型 当存在于较长的数据类型约束时,我们可以通过 type 关键字 为类型注解起别名,也可以通过接口来定义 type UserType = { name: string; age?: number }; const user: UserType = { name:

  • TypeScript 泛型的使用

    目录 1.简单的使用 2.在函数中使用泛型 3.在类中使用泛型 4.在泛型约束中使用类型参数 前言: 在JavaScript中,封装一个API可以具有多种用途,因为其实弱类型语言,但是就因为是弱类型可以最终得到的结果并不是我们想要的. TypeScript的出现正好中解决了这个问题,但是考虑到API的复用时,TypeScript又显得不是这么的灵活.这个时候可以使用any类型来解决不灵活的问题,但是又回到JavaScript中的问题,得到最终的结果可能不是预期的那个样子. 为了解决这种情况,Ty

  • typeScript 泛型使用和泛型接口结合

    目录 1.泛型是啥? 2.泛型类型 3.泛型接口 4.泛型类 5.泛型约束 6.泛型参数默认类型 7.泛型条件类型 typeScript 中新增的泛型概念.泛型使用.泛型与接口结合: 在实际应用中可能会遇到求最小值的问题,比如求数组中的最小值. 在 ts 中的就需要写两种方式,一种针对 number,另外一种针对字符串. 这样写不利于代码重用,项目较大时,性能较差,同时工作效率也低,所以在 ts 中引入了泛型概念. function getMin1(arr:number[]):number {

  • TypeScrip中泛型的案例详解

    泛型的定义 // 需求一: 泛型 可以支持不特定的数据类型, 要求,传入的参数和返回参数一致 // 这种方式虽然能实现传入和返回的参数一致,但是失去类型参数检验 /* function getData(value: any): any { return "success" } */ // 定义泛型解决需求一 // T表示泛型(这里的大写字母可以随便定义,但一般默认为T) 具体什么类型是调用这个方法的时候决定的 function getData<T>(value: T):T{

  • TypeScript泛型约束条件示例详解

    目录 什么是泛型 泛型的应用场景 泛型约束(限制条件) 泛型函数调用指定类型 总结 什么是泛型 两个值之间存在的对应关系,就可以用泛型来解决 泛型的应用场景 当一个函数的返回值的类型需要与此函数的参数类型想关联的时候,就需要使用泛型 例如 //约定此函数调用时必须传入一个数组,并返回数组第一项 function arrFn <T> (arr: T[]) :T|undefined { return arr[0] } const n = arrFn([1,2]) //number类型 const

  • TypeScript 泛型推断实现示例详解

    目录 前言 基础类型准备 最终使用的方式 基于Interface的实现 (失败了) 所有内容都基于type 实现 完整Demo 结束语 前言 最近做东西都在用ts,有时候写比较复杂的功能,如果不熟悉,类型写起来还是挺麻烦的.有这样一个功能,在这里,我们就不以我们现有的业务来举例了,我们还是已Animal举例,来说明场景.通过一个工厂来创建不同的动物实例.在这里我们借助泛型来实现类型的约束和动态推到指定类型. 基础类型准备 用一个枚举来定义Animal的类型 enum EAnimalType {

  • Spring实现泛型注入的示例详解

    目录 1.Spring泛型注入 2. 关于java泛型有四种Type GenericArrayType泛型数组类型 ParameterizedType参数化类型 TypeVariable 类型变量 WildcardType 通配符类型 1.Spring泛型注入 创建一个抽象泛型类BaseDao,有参数化类型T public abstract class BaseDao<T> { public abstract void eat(); } 每种动物有不同的行为,猫.狗 public class

  • Typescript装饰器AOP示例详解

    目录 在Typescript中使用装饰器 配置 类装饰器 方法装饰器 AOP(面向切面编程) 在Typescript中使用装饰器 上文中讲了装饰模式,今天来来介绍一些Typescript里面的装饰器,以及如何用装饰器来实现之前提及装饰模式,装饰器只是实现装饰模式的一种方式,并非唯一 配置 在Typescript要使用装饰器需要在tsconfig打开装饰器的语法 "compilerOptions": { "experimentalDecorators": true }

  • TypeScript 的条件类型使用示例详解

    目录 TypeScript 的条件类型使用方式 条件类型和 keyof 组合 在条件返回中使用 T 在类型输出中使用 T 时的联合类型 使用条件类型推断类型 总结 TypeScript 的条件类型使用方式 我们可以使用 TypeScript 中的条件类型来根据逻辑定义某些类型,就像是在编写代码那样. 它采用的语法和我们在 JavaScript 中熟悉的三元运算符很像:condition ? ifConditionTrue : ifConditionFalse. 我们来看看他是怎么工作的. 假设我

  • TypeScript类型级别和值级别示例详解

    目录 对值级别编程类型级别编程区分 类型级编程 挑战是如何工作的 挑战 对值级别编程类型级别编程区分 首先,让我们对值级别编程和类型级别编程进行重要区分. 值级别编程让我们编写将在生产中运行的代码即运行期,并为我们的用户提供有用的东西. 类型级别编程帮助我们确保代码在发布之前即编译期不包含错误,在运行期会被完全删除 JavaScript没有类型,所以所有JavaScript都是值级别的代码: // A simple Javascript function: function sum(a, b)

  • TypeScript新语法之infer extends示例详解

    目录 正文 模式匹配 提取枚举的值的类型 总结 正文 我们知道,TypeScript 支持 infer 来提取类型的一部分,通过模式匹配的方式. 模式匹配 比如元组类型提取最后一个元素的类型: type Last<Arr extends unknown[]> = Arr extends [...infer rest,infer Ele] ? Ele : never; 比如函数提取返回值类型: type GetReturnType<Func extends Function> = F

  • TypeScript前端上传文件到MinIO示例详解

    目录 什么是MinIO? 本地Docker部署测试服务器 上传的API TypeScript实现 1. XMLHttpRequest 2. Fetch API 3. Axios 从后端获取临时上传链接 上传文件 踩过的坑 1. presignedPutObject方式上传提交的方法必须得是PUT 2. 直接发送File即可 3. 使用Axios上传的时候,需要自己把Content-Type填写成为file.type 示例代码 什么是MinIO? MinIO 是一款高性能.分布式的对象存储系统.

  • TypeScript类型断言VS类型守卫示例详解

    目录 类型断言 类型守卫 使用 in 关键字 使用 instanceof 关键字 使用 typeof 关键字 自定义类型守卫 总结 类型断言 类型断言有两种写法,分别为value as Type和<Type>value,它让 TypeScript 编译器将 value 当作 Type 类型.类型断言是一个编译时特性,不进行类型转换,因此不会影响变量在运行时的数据类型.如果某变量是 any 类型,但现在你知道它确切的数据类型,使用类型断言能让 IDE 有代码提示的能力,也能让 TypeScrip

  • Java中泛型的示例详解

    目录 泛型概述 使用泛型的好处 泛型的定义与使用 定义和使用含有泛型的类 含有泛型的方法 含有泛型的接口 泛型通配符 通配符基本使用 通配符高级使用----受限泛型 泛型概述 我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型.当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换. 大家观察下面代码: public class GenericDemo { public static void main(String[] args) {

  • TypeScript实现十大排序算法之冒泡排序示例详解

    目录 一. 冒泡排序的定义 二. 冒泡排序的流程 三. 冒泡排序的图解 四. 冒泡排序的代码 五. 冒泡排序的时间复杂度 六. 冒泡排序的总结 一. 冒泡排序的定义 冒泡排序是一种简单的排序方法. 基本思路是通过两两比较相邻的元素并交换它们的位置,从而使整个序列按照顺序排列. 该算法一趟排序后,最大值总是会移到数组最后面,那么接下来就不用再考虑这个最大值. 一直重复这样的操作,最终就可以得到排序完成的数组. 这种算法是稳定的,即相等元素的相对位置不会发生变化. 而且在最坏情况下,时间复杂度为O(

随机推荐