前言

最近做东西都在用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的实现 (失败了)

  1. 接着我们创建一个interface 来定义动物基础接口
export interface IAnimal<T extends EAnimalType> extends IAnimalExtra<T> {
id: number; // 编号
name: string; // 名称
type: T; // 类型
}

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

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

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

  1. 最后用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中的某个类型,遍历。之后我们专门写篇文章,介绍下这块相关的内容。

  1. 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了吗?

如果你觉得该文章不错,不妨

1、点赞,让更多的人也能看到这篇内容

2、关注我,让我们成为长期关系

3、关注公众号「前端有话说」,里面已有多篇原创文章,和开发工具,欢迎各位的关注,第一时间阅读我的文章

TS 泛型推断好难啊,看看你能写出来不的更多相关文章

  1. 从fastjson多层泛型嵌套解析,看jdk泛型推断

    给你一组json数据结构,你把它解析出来到项目中,你会怎么做? // data1 sample { "code" : "1", "msg" ...

  2. Java1.5泛型指南中文版(Java1.5 Generic Tutorial)

    Java1.5泛型指南中文版(Java1.5 Generic Tutorial) 英文版pdf下载链接:http://java.sun.com/j2se/1.5/pdf/generics-tutori ...

  3. 泛型(java菜鸟的课堂笔记)

                1. 泛型的简单运 用和意义   2. 泛型的上限与下限   3. 泛型和 子类继承的限制   4. 泛型类和泛型 方法   5. 泛型嵌套和泛型擦除             ...

  4. Typescript高级类型与泛型难点详解

    最近做的TS分享,到了高级类型这一块.通过琢磨和实验还是挖掘出了一些深层的东西,在此处做一下记录,也分享给各位热爱前端的小伙伴.   其实在学习TS之前就要明确以下几点:   1. typescrip ...

  5. JAVA泛型知识(一)

    Java泛型知识(二)<? extends T>和<? super T> Java1.5泛型指南中文版(Java1.5 Generic Tutorial) 目        录 ...

  6. TS基础应用 & Hook中的TS

    说在前面 本文难度偏中下,涉及到的点大多为如何在项目中合理应用ts,小部分会涉及一些原理,受众面较广,有无TS基础均可放心食用. **>>>> 阅完本文,您可能会收获到< ...

  7. java 16-6 泛型

    ArrayList存储字符串并遍历 我们按照正常的写法来写这个程序, 结果确出错了. 为什么呢? 因为我们开始存储的时候,存储了String和Integer两种类型的数据. 而在遍历的时候,我们把它们 ...

  8. Android(java)学习笔记89:泛型概述和基本使用

    package cn.itcast_01; import java.util.ArrayList; import java.util.Iterator; /* * ArrayList存储字符串并遍历 ...

  9. [改善Java代码]不同的场景使用不同的泛型通配符

    Java泛型支持通配符(Wildcard),可以单独使用一个"?"表示任意类,也可以使用extends关键字标识某一类(接口)的子类型,还可以使用super关键字标识某一类(接口) ...

随机推荐

  1. MATLAB调用NASTRAN的连续计算解决方案

    使用MATLAB调用NASTRAN提交bdf文件计算的方法笔者试过几种,分别做一下介绍 MATLAB直接调用操作系统命令 该方法对单次计算可能方便些,定义一个字符串,并使用eval命令调用,具体格式为 ...

  2. HashMap 中的 hash 函数

    1. 什么是 hash 函数 hash 函数,即散列函数,或叫哈希函数.它可以将不定长的输入,通过散列算法转换成一个定长的输出,这个输出就是散列值.需要注意的是,不同的输入通过散列函数,也可能会得到同 ...

  3. Spring Ioc源码分析系列--@Autowired注解的实现原理

    Spring Ioc源码分析系列--@Autowired注解的实现原理 前言 前面系列文章分析了一把Spring Ioc的源码,是不是云里雾里,感觉并没有跟实际开发搭上半毛钱关系?看了一遍下来,对我的 ...

  4. jenkins 流水线自动化部署 手动下载安装插件包

    如果有些插件不能通过可选插件安装,可以进行选择高级并上传插件包,插件包链接地址为:http://updates.jenkins-ci.org/download/plugins/ 同时在高级中可以更换下 ...

  5. 【zigbee无线通信模块步步详解】ZigBee3.0模块建立远程网络控制方法

    本文以路灯控制应用为例,简述ZigBee3.0模块使用流程. 一.建立网络 1.通过USB转串口模块将出厂的ZigBee自组网模块连接,打开上位机软件"E180-ZG120A-Setting ...

  6. Linux安装netstat命令

    Linux安装netstat命令 1.查找netstat命令所属的依赖包 [root@localhost ~]# yum provides netstat netstat命令的安装包为net-tool ...

  7. Linux系列之linux访问windows文件

    Linux永久挂载windows共享文件 Linux系统必须安装samba-client Linux服务器必须能访问到Windows的共享文件服务的(445端口) 1.Windows共享文件 2.测试 ...

  8. JVM学习笔记-从底层了解程序运行(一)

    1:JVM基础知识 什么是JVM 1. java虚拟机,跨语言的平台,实现java跨平台 2. 可以实现多种语言跨平台,只要该语言可以编译成.class文件 3. 解释执行.class文件 java是 ...

  9. 23.Nginx+keepalived负载均衡高可用

    Nginx+keepalived负载均衡高可用 结构图 环境: 主 服务器:192.168.239.10 备 服务器:192.168.239.20 Web 服务器1:192.168.239.40 We ...

  10. [二进制漏洞]栈(Stack)溢出漏洞 Linux篇

    目录 [二进制漏洞]栈(Stack)溢出漏洞 Linux篇 前言 堆栈 堆栈(Stack)概念 堆栈数据存储方式 函数调用 函数调用C语言代码 函数调用过程GDB调试 函数Call返回原理 函数栈帧 ...