TS 泛型推断好难啊,看看你能写出来不
前言
最近做东西都在用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了吗?
如果你觉得该文章不错,不妨
1、点赞,让更多的人也能看到这篇内容
2、关注我,让我们成为长期关系
3、关注公众号「前端有话说」,里面已有多篇原创文章,和开发工具,欢迎各位的关注,第一时间阅读我的文章
TS 泛型推断好难啊,看看你能写出来不的更多相关文章
- 从fastjson多层泛型嵌套解析,看jdk泛型推断
给你一组json数据结构,你把它解析出来到项目中,你会怎么做? // data1 sample { "code" : "1", "msg" ...
- Java1.5泛型指南中文版(Java1.5 Generic Tutorial)
Java1.5泛型指南中文版(Java1.5 Generic Tutorial) 英文版pdf下载链接:http://java.sun.com/j2se/1.5/pdf/generics-tutori ...
- 泛型(java菜鸟的课堂笔记)
1. 泛型的简单运 用和意义 2. 泛型的上限与下限 3. 泛型和 子类继承的限制 4. 泛型类和泛型 方法 5. 泛型嵌套和泛型擦除 ...
- Typescript高级类型与泛型难点详解
最近做的TS分享,到了高级类型这一块.通过琢磨和实验还是挖掘出了一些深层的东西,在此处做一下记录,也分享给各位热爱前端的小伙伴. 其实在学习TS之前就要明确以下几点: 1. typescrip ...
- JAVA泛型知识(一)
Java泛型知识(二)<? extends T>和<? super T> Java1.5泛型指南中文版(Java1.5 Generic Tutorial) 目 录 ...
- TS基础应用 & Hook中的TS
说在前面 本文难度偏中下,涉及到的点大多为如何在项目中合理应用ts,小部分会涉及一些原理,受众面较广,有无TS基础均可放心食用. **>>>> 阅完本文,您可能会收获到< ...
- java 16-6 泛型
ArrayList存储字符串并遍历 我们按照正常的写法来写这个程序, 结果确出错了. 为什么呢? 因为我们开始存储的时候,存储了String和Integer两种类型的数据. 而在遍历的时候,我们把它们 ...
- Android(java)学习笔记89:泛型概述和基本使用
package cn.itcast_01; import java.util.ArrayList; import java.util.Iterator; /* * ArrayList存储字符串并遍历 ...
- [改善Java代码]不同的场景使用不同的泛型通配符
Java泛型支持通配符(Wildcard),可以单独使用一个"?"表示任意类,也可以使用extends关键字标识某一类(接口)的子类型,还可以使用super关键字标识某一类(接口) ...
随机推荐
- ACM 刷题记录
HDU Multi-University Training Contest 题目来源 题目 知识点 时间复杂度 完成情况 2019 Contest8 A Acesrc and Cube Hyperne ...
- golang 方法接收者
[定义]: golang的方法(Method)是一个带有receiver的函数Function,Receiver是一个特定的struct类型,当你将函数Function附加到该receiver, 这个 ...
- 深度学习与CV教程(6) | 神经网络训练技巧 (上)
作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/37 本文地址:http://www.showmeai.tech/article-det ...
- vue面试总结-2022
1.vue生命周期及各周期得特点 beforCreate 特点: 初始化实例,不能使用data和methods.ref 示例 beforeCreate: function () { console.g ...
- Flink使用Pod Template将状态快照(Checkpoint、Savepoint)存储在NFS
背景 Flink 版本 1.13.3,使用 native k8s 部署模式,原采用 HDFS 作为状态快照(Checkpoint.Savepoint)的存储地址,但是由于仅使用了其 HDFS 作为状态 ...
- 耗时半年,Eoapi 终于正式发布 API 工具的插件广场
这是我们的第一篇月报,每个月和每个来之不易的开发者分享产品故事以及产品进展. 在 5.20 这个极具中国特色的"节日"里,Eoapi 发布了 1.0.0 版,三个程序员掉了半年 ...
- ROS基本程序实现
0.前言 现在介绍ROS基本程序实现的教程有很多,步骤无非就是建工作空间,编译,创建功能包,创建文件和修改CMakeList,之后再编译.运行程序这几步.但是这些教程中很多在文件夹切换的很混乱,导致会 ...
- Docker安装Jenkins打包Maven项目为Docker镜像并运行【保姆级图文教学】
一.前言 Jenkins作为CI.CD的先驱者,虽然现在的风头没有Gitlab强了,但是还是老当益壮,很多中小公司还是使用比较广泛的.最近小编经历了一次Jenkins发包,感觉还不错,所以自己学习了一 ...
- C语言- 基础数据结构和算法 - 栈的顺序存储
听黑马程序员教程<基础数据结构和算法 (C版本)>, 照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友 ...
- mybatis踩过的坑
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "- ...