前言

最近做东西都在用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. Vben Admin 源码学习:项目初始化

    0x00 前言 Vue-Vben-Admin 是一个免费开源的中后台模版.使用了最新的vue3,vite2,TypeScript等主流技术开发,开箱即用的中后台前端解决方案考. 本系列本着学习参考的目 ...

  2. 用树莓派USB摄像头做个监控

    [前言] 看着阴暗的角落里吃灰噎到嗓子眼的树莓派,一起陪伴的时光历历在目,往事逐渐涌上心头,每每触及此处,内心总会升腾起阵阵怜悯之情... 我这有两个设备,一个是积灰已久的树莓派,另一个是积灰已久的U ...

  3. Go微服务框架go-kratos实战04:kratos中服务注册和服务发现的使用

    一.简介 关于服务注册和服务发现介绍,我前面的文章有介绍过 - 服务注册和发现的文章. 作为服务中心的软件有很多,比如 etcd,consul,nacos,zookeeper 等都可以作为服务中心. ...

  4. CabloyJS究竟是一款什么样的框架

    CabloyJS是什么样的框架 CabloyJS 是一款自带工作流引擎的 Node.js 全栈框架,一款面向开发者的低代码开发平台,更是一款兼具低代码的开箱即用和专业代码的灵活定制的 PAAS 平台 ...

  5. Halodoc使用 Apache Hudi 构建 Lakehouse的关键经验

    Halodoc 数据工程已经从传统的数据平台 1.0 发展到使用 LakeHouse 架构的现代数据平台 2.0 的改造.在我们之前的博客中,我们提到了我们如何在 Halodoc 实施 Lakehou ...

  6. Linux云服务器 磁盘分区失败

    背景:阿里云服务器,df -h  /dev/vda1有40g,然后想把这40g拆一下,拆成几个分区,挂载到不同的路径下. 行动:需要先卸载挂载的盘,但是umount失败,通过fuser删除盘上的服务失 ...

  7. C语言学习之我见-strlen()字符串长度函数

    strlen()函数,负责给出字符串的长度.注意是字符串的长度,不是字符数组的长度. (1)函数原型: size_t __cdecl strlen(const char *_Str); (2)头文件` ...

  8. Mybatisi和Spring整合源码分析

    一.MybatisSpring的使用 1.创建 Maven 工程. 2.添加依赖,代码如下 <dependency> <groupId>org.mybatis</grou ...

  9. Kolla部署Pike版本的OpenStack-allinone云平台

    1 openstack 概述 openstack概述 : OpenStack是一个NASA美国国家航空航天局和Rackspace合作研发 的,以Apache许可证授权,并且是一个自由软件和开放源代码项 ...

  10. SQL语句的整理

    mysql语句的整理 1.SQL DML 和 DDL 可以把 SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL). SQL (结构化查询语言)是用于执行查询的语法.但是 SQ ...