Typescript 简单类型体操之”infer的妙用“--TS类型单词提取

前言

  • 我经常遇到一个枚举需要与某个类型相匹配的情况,然后就产生了强耦合,每次修改那个类型,相应的枚举也需要跟着改,有时候实在嫌麻烦,也就只能使用any或者其他简单类型来声明了,舍弃了枚举,但是这就使得类型模糊不清,TS 的意义也就无法很好的体现了
  • 在无意间,了解到了 infer 关键词,学习之后,发现可以解决以上问题,一起来看下吧
  • (如果这篇文章写得哪里不好,欢迎友好评论)

代码开始!!

假设有这么一段原始功能

  • 【期望需求】 : findPuzzle 函数中的 pType 参数能够是符合 puzzleTypes 对象的 keys 中表示动物的单词,即 pType 只能是 Cat/Dog/Rabbit,否则 TS 就应该报类型错误,而且这个 puzzleTypes 对象是满足一定规则的且经常会去改动的数据,即其中的 key 可能以 puzzle${animal}Toy 这种规则动态出现
  • 【原因描述】:我们曾经的写法就会像下面的代码一样,用 string 去声明 pType 类型,因为我们当时只会用静态枚举,使用静态枚举类型的话,每次修改 puzzleTypes 数据后,对应的枚举也要一起修改,这就很麻烦了;当然我们也想到过 keyof,可惜keyof只能把完整的key提取出来,我们只希望把其中的某个单词提取出来作为动态枚举来使用
typescript 复制代码
interface PuzzleTypes {
  puzzleCatToy: string;
  puzzleDogToy: string;
  puzzleRabbitToy: string;  
}
// 我们假设这个 mock 数据实际上是一份满足一定规则的且经常会去改动的数据,即其中的 key 可能以 `puzzle${animal}Toy` 这种规则动态出现
const puzzleTypes: PuzzleTypes = {
    puzzleCatToy: 'red-cat-puzzle',
    puzzleDogToy: 'point-dog-puzzle',
    puzzleRabbitToy: 'purple-rabbit-puzzle',
}

interface ParamsFindPuzzle {
  pType: string
}
const findPuzzle = ({pType}:ParamsFindPuzzle) => {
    return Object.keys(puzzleTypes).find(pk => pk.includes(pType))
}

// -- 调用
const canfind = findPuzzle({pType: 'bear'})

新方案

  • 1 - 先认识新方案的基础用法
typescript 复制代码
type ExtractAnimal<S extends string> = S extends `puzzleDogToy` ? 'Dog' : 'not find'

// 通过上面的 ExtractAnimal 类型声明,我们这里得到的 PuzzleAnimal 类型是 'not find'
type PuzzleAnimal = ExtractAnimal<'bear'>
  • 2 - 继续改进为动态的模版字符串声明方式
    • 从这里就能看出来了,借用 infer 关键词可以实现提取部分字符串来作为类型
    • 那 infer 的含义是:类型推断,infer 只能用在 extends 判断且为true的子句中
typescipt 复制代码
type ExtractAnimal<S extends string> = S extends `puzzle${infer R}Toy` ? R : S

// 此时 PuzzleAnimal 类型是我们传递的 'bear',也就是上面 ExtractAnimal 范型中的 S
type PuzzleAnimal = ExtractAnimal<'bear'>
  • 3 - 运用到我们原来的代码中
typescript 复制代码
interface PuzzleTypes {
  puzzleCatToy: string; 
  puzzleDogToy: string;
  puzzleRabbitToy: string;
}

const puzzleTypes = {
  puzzleCatToy: 'red-cat-puzzle',
  puzzleDogToy: 'point-dog-puzzle',  
  puzzleRabbitToy: 'purple-rabbit-puzzle',
};

type PuzzleKeys = keyof PuzzleTypes; 

type PuzzleAnimals = PuzzleKeys extends `puzzle${infer S}Toy` ? S : never;
// --

interface ParamsFindPuzzle {
  pType: PuzzleAnimals
}
const findPuzzle = ({pType}:ParamsFindPuzzle) => {
    return Object.keys(puzzleTypes).find(pk => pk.includes(pType))
}

// -- 调用:此时这里的 pType 传 'Bear' 就会报 TS 类型错误啦
const canfind = findPuzzle({ pType: 'Bear' })
  • 如此一来,以后我们对 PuzzleTypes 进行增删(熟悉名规则不变的情况下),findPuzzle({ pType: 'Bear' }) 函数中的 pType 参数类型声明也就会随之发生变化了

还有彩蛋哦!!

如果 puzzleTypes 是数组呢,怎么办?
  • 直接上代码
typescript 复制代码
// as const 也是关键,只有静态的才能被下面的 infer 进行推断提取
const puzzleTypes = ['puzzle-cat', 'puzzle-dog', 'puzzle-rabbit'] as const;

type ExtractAnimals<T extends string> = T extends `puzzle-${infer S}` ? S : T;
// --

interface ParamsFindPuzzle {
  pType: ExtractAnimals<typeof puzzleTypes[number]> // 通过 typeof puzzleTypes[number] 获取对应的类型
}
const findPuzzle = ({pType}:ParamsFindPuzzle) => {
    return Object.keys(puzzleTypes).find(pk => pk.includes(pType))
}

// -- 调用:同样的,这里会有清晰 TS 类型提示
const canfind = findPuzzle({ pType: 'bear' })

重点重提!

【infer】
  • 的含义:
    • 类型推断时,infer 只能用在 extends 判断且为 true 的子句中,可用于推断相应的类型
    • 虽然这里的例子属于比较简单的,但是 infer 是很强大的,灵活运用 infer 可以让 TS 写的更得体,可以解决我们代码中很多类型声明模糊不清的问题
相关推荐
天人合一peng13 分钟前
Unity中button 和toggle监听事件函数有无参数
前端·unity·游戏引擎
会飞的战斗鸡31 分钟前
JS中的链表(含leetcode例题)
javascript·leetcode·链表
方也_arkling1 小时前
别名路径联想提示。@/统一文件路径的配置
前端·javascript
毕设源码-朱学姐1 小时前
【开题答辩全过程】以 基于web教师继续教育系统的设计与实现为例,包含答辩的问题和答案
前端
qq_177767371 小时前
React Native鸿蒙跨平台剧集管理应用实现,包含主应用组件、剧集列表、分类筛选、搜索排序等功能模块
javascript·react native·react.js·交互·harmonyos
qq_177767371 小时前
React Native鸿蒙跨平台自定义复选框组件,通过样式数组实现选中/未选中状态的样式切换,使用链式调用替代样式数组,实现状态驱动的样式变化
javascript·react native·react.js·架构·ecmascript·harmonyos·媒体
web打印社区1 小时前
web-print-pdf:突破浏览器限制,实现专业级Web静默打印
前端·javascript·vue.js·electron·html
RFCEO2 小时前
前端编程 课程十三、:CSS核心基础1:CSS选择器
前端·css·css基础选择器详细教程·css类选择器使用方法·css类选择器命名规范·css后代选择器·精准选中嵌套元素
烬头88212 小时前
React Native鸿蒙跨平台采用了函数式组件的形式,通过 props 接收分类数据,使用 TouchableOpacity实现了点击交互效果
javascript·react native·react.js·ecmascript·交互·harmonyos
Amumu121382 小时前
Vuex介绍
前端·javascript·vue.js