TypeScript 类型推导还可以这么用?
一、为什么需要TypeScript类型推导
肯定是为了节省我们的代码,减少冗余,下面举一个例子,写一个递减函数
下面代码中,写了三个类型都是number,那么我们是不是可以思考,如何减少冗余呢,ts给咱们提供了泛型
正常书写
const desc = (a: number): number => {
return --a
}
desc(2) // 1
泛型
const desc = <T>(a: T): number => {
return --a
}
desc(3) // 2
这个例子在项目中的应用场景可谓之少之又少,也可以说,压根不可能出现。
那么衍生出一个问题:有没有更好的方法,自动推导我特定的入参返回特定类型。
有的兄弟,有的!!
我先把公共代码抽出来
// 公共属性
type Pet = {
name: string
age: number
}
// 映射类型
type OptionsMap = {
dog?: {
bark: string
barkVolume: number
} & Pet
cat?: {
favoriteToy: string
furLength: 'short' | 'medium' | 'long'
} & Pet
}
场景:
- 组件封装,表单、表单组件类型封装,例如通过表单项type来确定是input、select...就可以明确props类型了
- 业务逻辑封装,在重复的业务逻辑中我们通常会抽离,通过key去调用,那么对应的参数不同,也会用到泛型、自动推导
二、映射 + 类型推导
我们可以通过映射、类型推导相互配合就能得到特定的类型了,废话少说,上代码
2.1. 先来一个函数案例,通过参数1推导出参数2
const fn = <T extends keyof OptionsMap>(type: T, props: OptionsMap[T]) => {}
fn('cat', {})
/*
类型“{}”的参数不能赋给类型“{ favoriteToy: string; furLength: "short" | "medium" | "long"; } & Pet”的参数。
类型“{}”缺少类型“{ favoriteToy: string; furLength: "short" | "medium" | "long"; }”中的以下属性: favoriteToy, furLengthts-plugin(2345)
*/
- 上面代码声明了宠物映射属性,通过函数的第一个参数去映射中自动匹配第二个参数
- 恭喜你现在已经掌握类型推导的基础了
2.2. 下面我们再来一个写一个生成集合,里面数据项都是通过推导出来的
type SinglePet<T extends keyof OptionsMap> = {
type: keyof OptionsMap
options: OptionsMap[T]
}
type Pets = SinglePet<keyof OptionsMap>[]
const petHome: Pets = [
{
type: 'dog',
options: {
name: '旺财',
age: 2,
bark: '汪汪汪',
barkVolume: 2,
},
},
{
type: 'cat',
options: {
name: '小白',
age: 1,
favoriteToy: '玩具鸟',
furLength: 'short',
},
},
]
数组里的每一项options,我们都是通过type推导出来的。
三、extends + 类型推导
extends和映射作用都是为了自动推导出另一个属性或者另一个类型,作用是一样的,具体使用哪种,可以根据项目而定
type PetKeys = 'dog' | 'cat' | 'fish' | 'bird'
type PetOptions<T extends PetKeys> = T extends 'dog'
? OptionsMap['dog']
: T extends 'cat'
? OptionsMap['cat']
: {
text: number
}
type OptionItem<T extends PetKeys> = PetOptions<T>
type Item<T extends PetKeys = PetKeys> = {
type: T
options: OptionItem<T>
}
type PetHome = Item[]
const petHome: PetHome = [
{
type: 'dog',
options: {
name: '旺财',
age: 2,
bark: '汪汪汪',
barkVolume: 10,
},
},
{
type: 'bird',
options: {
text: 1,
},
},
{
type: 'cat',
options: {
name: '小白',
age: 2,
favoriteToy: '玩具鸟',
barkVolume: 5,
furLength: 'long',
},
},
]
上面这段代码主要是使用extend判断对应的类型,在项目中封装很常见,也利于后期拓展,其中核心代码为
type Item<T extends PetKeys = PetKeys> = {
type: T options: OptionItem<T>
}
四、总结
类型推导可以不用,不能不会,有ts的项目必出现这种场景,当然也可以用AI 😂
最后告诉大家几个泛型、推导的注意事项
泛型参数,也就是本文中的T,如果一个使用项中的T已经明确类型了,其他地方也会跟着明确。
泛型参数一旦确定就不会再变,所有咱们如果使用默认值时,引用的类型必须要传递,否则就是按默认值算了。
最后祝大家日入过万,给俺点点关注、点点赞