• 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 {
    
      // TODO 根据业务具体实现
      return {} as IAnimal;
    }
    
    // 根据泛型创建狗狗的实例
    const dog = createAnimalFactory();
    dog.shoutLoudly();
    
    // 根据泛型创建鸟的实例
    const bird = createAnimalFactory();
    bird.fly()
    
    

    基于Interface的实现 (失败了)

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

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

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

    101.png

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

    1. 最后用type 去替代 IAnimalExtra
    export type IAnimalExtra 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 报错了

    102.png

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

    所有内容都基于type 实现

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

    export type IAnimal 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 extends EAnimalType> = {
      [c in keyof AnimalMap[T]]: AnimalMap[T][c];
    }
    
    export type IAnimal 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、关注公众号「前端有话说」,里面已有多篇原创文章,和开发工具,欢迎各位的关注,第一时间阅读我的文章

  • 相关阅读:
    第五节 Electron 模块介绍 remote模块详细介绍
    【Github】将本地仓库同步到github上
    Aspose.GIS 22.11.0 for .NET Crack
    苹果上架Guideline 4.3 - Design
    超声波传感器(CHx01) 学习笔记 Ⅵ - 原始数据
    微信小程序 提交表单
    Cadence orcad 批量设置原理图标题栏
    CGAL::2D Arrangements-7
    揭秘“消费即投资,每日返现”的循环购模式
    3d可视化智慧机房管理系统避免风险损失
  • 原文地址:https://www.cnblogs.com/qiaojie/p/16594191.html