前言

最近做东西都在用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];
}

101.png

我们这样写,发现调试控制台报了很多错,具体分析了下错误,接口不支持这种功能。接着我们尝试,改成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> 报错了

102.png

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

所有内容都基于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、关注公众号「前端有话说」,里面已有多篇原创文章,和开发工具,欢迎各位的关注,第一时间阅读我的文章

posted @ 2022-08-17 10:34 快乐的开发者 阅读(1005) 评论(2) 推荐(4) 编辑
摘要: React重新渲染指南 React 重新渲染的综合指南。该指南解释了什么是重新渲染,什么是必要的和不必要的重新渲染,什么情况下会触发 React 组件重新渲染。 阅读全文
posted @ 2022-08-10 23:27 快乐的开发者 阅读(1372) 评论(0) 推荐(3) 编辑
摘要: nginx 部署前端资源的最佳方案 前言 最近刚来一个运维小伙伴,做线上环境的部署的时候,前端更新资源后,总是需要清缓存才能看到个更新后的结果。客户那边也反馈更新了功能,看不到。 方案 前端小伙伴应该都知道浏览器的缓存策略,协商缓存和强缓存,如有不清楚的地方,可以看看这篇文章[聊聊浏览器缓存] (https://juejin.cn/p 阅读全文
posted @ 2022-06-08 10:35 快乐的开发者 阅读(1572) 评论(5) 推荐(1) 编辑
摘要: 测试同学问我为什么每次让我清缓存(聊聊浏览器的缓存) 前言 最近一个好奇的测试同学问我,你们前端开发完成后,每次都让我们清缓存或者Ctrl+f5强制刷新,我能不能每次不用强制刷新,就能看到你们更新的内容呢。我说是可以做到的,我来跟你讲讲浏览器的缓存策略。我相信应该有不少的同学在工作中都会遇到这种情况,让你的测试同学清缓存等。 缓存 浏览器缓存是为了节约 阅读全文
posted @ 2022-01-21 16:48 快乐的开发者 阅读(1274) 评论(3) 推荐(5) 编辑
摘要: (开源)给图片编辑器添加了辅助线 前言 上篇我们介绍了做的图片编辑器,大部分工具类的软件都有辅助线,方便拖拽元素的时候对齐,能让我们快速的做出漂亮的图片。 这两天给编辑器加上了辅助线, 辅助线实现过程稍微有些复杂,我们一步步说下实现过程。 演示 演示地址 实现流程 原理讲解 左侧辅助线出现 我们以节点2为移动的元素,通过上面的图观察 阅读全文
posted @ 2021-08-19 14:02 快乐的开发者 阅读(795) 评论(0) 推荐(0) 编辑
摘要: (开源)两个周末写了个图片编辑器 一款开源图片编辑器,采用React + Typescript + React-knova 框架开发. 阅读全文
posted @ 2021-08-19 13:52 快乐的开发者 阅读(972) 评论(0) 推荐(0) 编辑
摘要: 项目介绍 "Taro_Mall" 是一款多端开源在线商城应用程序,后台是基于litemall基础上进行开发,前端采用Taro框架编写,现已全部完成小程序和h5移动端,后续会对APP,淘宝,头条,百度小程序进行适配。Taro_Mall已经完成了 litemall 前端的所有功能 扫码体验 由于小程序没 阅读全文
posted @ 2020-03-06 23:24 快乐的开发者 阅读(5039) 评论(0) 推荐(1) 编辑
摘要: 介绍 "Taro_Mall" 是一款多端开源在线商城应用程序,后台是基于litemall基础上进行开发,前端采用Taro框架编写,现已全部完成小程序和h5移动端,后续会对APP,淘宝,头条,百度小程序进行适配。Taro_Mally已经完成了 litemall 前端的所有功能 扫码体验 小程序 由于小 阅读全文
posted @ 2020-03-04 23:14 快乐的开发者 阅读(2536) 评论(0) 推荐(0) 编辑
摘要: 前沿 无代码开发是一种无需编写代码或通过少量代码就可以快速生成应用程序的开发平台。它可以快速交付应用程序,解决传统软件开发模式带来的周期长、成本高等问题。无代码开发平台,允许用户通过拖拽和放置组件的方式来构建应用界面。 brick 是一个无代码开发平台,采用 React+Typescript+Low 阅读全文
posted @ 2024-05-24 10:23 快乐的开发者 阅读(117) 评论(0) 推荐(0) 编辑
摘要: pnpm 的 workspace 实现 monorepo 工程 前端多个包管理的的方式一般都是采用`monorepo`的方式去管理,之前都是使用的`lerna`的workspace去管理。这段时间包管理切换到了`pnpm`上,它也有worksapce,可以支持`monorepo`。 阅读全文
posted @ 2022-08-12 11:14 快乐的开发者 阅读(1970) 评论(0) 推荐(0) 编辑
摘要: 微前端(qiankun)主应用共享React组件 前言 最近需要重构一个老项目,定的方案用微前端去改造。主应用是老的项目,微应用是新的项目,由于重构时间比较紧张,子应用还需要使用父应用的一些组件。过程中遇到一些问题,记录一下。 方案 我们知道qiankun,可以通过props通信传递数据,把组件通过props传递过去不就行了。来开始改造我们的代码 阅读全文
posted @ 2022-06-08 16:50 快乐的开发者 阅读(1777) 评论(0) 推荐(1) 编辑
摘要: 前端也要会【异或运算】 前言 在前端的日常开发当中,我们很少用到异或运算,但在一些框架源码当中,会有用到异或运算。我们在阅读源码的时候,会造成代码的不理解,今天我们介绍一下异或运算。 概念 异或,英文为exclusive OR,缩写成eor. 异或(eor)是一个数学运算符。它应用于逻辑运算。异或的数学符号为“⊕”,计算机 阅读全文
posted @ 2021-09-27 14:03 快乐的开发者 阅读(1137) 评论(1) 推荐(2) 编辑
点击右上角即可分享
微信分享提示