社攻联盟 - 大型多人在线游戏活动平台

社攻联盟 - 大型多人在线游戏活动平台

shape

理解TypeScript中的infer关键字

  • Home
  • 跨服战场
  • 理解TypeScript中的infer关键字
  • 2025-12-29 22:20:14
  • admin

前言

infer是在typescript 2.8中新增的关键字,距离现在3.9.3已经有两年出头了,趁着今天刚好使用了infer,所以好好整理一番

infer

infer可以在extends的条件语句中推断待推断的类型

例如在文档的示例中,使用infer来推断函数的返回值类型

type ReturnType = T extends (...args: any[]) => infer R ? R : any;

type func = () => number;

type variable = string;

type funcReturnType = ReturnType; // funcReturnType 类型为 number

type varReturnType = ReturnType; // varReturnType 类型为 string

在这个例子中,infer R代表待推断的返回值类型,如果T是一个函数,则返回函数的返回值,否则返回any

仅仅通过这一个例子,是很难看出infer是用来干什么的,还需要多看几个例子

infer解包

infer的作用不止是推断返回值,还可以解包,我觉得这是比较常用的

假如想在获取数组里的元素类型,在不会infer之前我是这样做的

type Ids = number[];

type Names = string[];

type Unpacked = T extends Names ? string : T extends Ids ? number : T;

type idType = Unpacked; // idType 类型为 number

type nameType = Unpacked; // nameType 类型为string

上次我写了20多行,就为了获取一堆各种不同类型的数组里的元素类型,然而如果使用infer来解包,会变得十分简单

type Unpacked = T extends (infer R)[] ? R : T;

type idType = Unpacked; // idType 类型为 number

type nameType = Unpacked; // nameType 类型为string

这里T extends (infer R)[] ? R : T的意思是,如果T是某个待推断类型的数组,则返回推断的类型,否则返回T

再比如,想要获取一个Promise类型中的xxx类型,在不使用infer的情况下我想不到何解

type Response = Promise;

type Unpacked = T extends Promise ? R : T;

type resType = Unpacked; // resType 类型为number[]

infer推断联合类型

还是官方文档的例子

type Foo = T extends { a: infer U; b: infer U } ? U : never;

type T10 = Foo<{ a: string; b: string }>; // T10类型为 string

type T11 = Foo<{ a: string; b: number }>; // T11类型为 string | number

同一个类型变量在推断的值有多种情况的时候会推断为联合类型,针对这个特性,很方便的可以将元组转为联合类型

type ElementOf = T extends (infer R)[] ? R : never;

type TTuple = [string, number];

type Union = ElementOf; // Union 类型为 string | number

React中infer的使用

在React的typescript源码中应该常常使用infer

就拿useReducer来举例子,如果我们这样使用useReducer

const reducer = (x: number) => x + 1;

const [state, dispatch] = useReducer(reducer, '');

// Argument of type "" is not assignable to parameter of type 'number'.

这里useReducer会报一个类型错误,说""不能赋值给number类型

那么React这里是如何通过reducer函数的类型来判断state的类型呢?

查看userReducer的定义,定义如下

function useReducer, I>(

reducer: R,

// ReducerState 推断类型

initializerArg: I & ReducerState,

initializer: (arg: I & ReducerState) => ReducerState

): [ReducerState, Dispatch>];

// infer推断

type ReducerState> = R extends Reducer

? S

: never;

// Reducer类型

type Reducer = (prevState: S, action: A) => S;

一切明了了,使用了infer推断reducer函数里的state参数类型

我今天遇见的问题

今天使用ant-design-chart,库里没有把Ref的定义导出,所以只能自己取了

// 已知

type ref = React.MutableRefObject;

// 求 ???

const chartRef = useRef()

有了上面的学习,这里就很简单了,只需要取出React.MutableRefObject里的内容,一行infer搞定

// infer推断

type ChartRef = T extends React.MutableRefObject ? P : never;

const chartRef = useRef>()

总结

infer是非常有用的,如果想要摆脱仅仅是在写带类型的javascript,高级特性一定要了解

我可能一年前就看见infer了,一直没有好好学,原因除了自己懒,还有就是水平确实不够,今年再学明显感觉不同了。

再推荐一篇很好的文章,我也是看了这篇文章才好好学习了一下infer,这篇文章讲的更复杂一点

Vue3 跟着尤雨溪学 TypeScript 之 Ref 类型从零实现

题外话 分享一道比较复杂的练习题

原题就不贴出了,在这里可以看见 github

分享一下我的思路

首先先取得函数的名字,通过extends关键字可以判断是否是函数,是返回键名,不是返回never,最后使用映射类型[keyof T]的方式来获取键名的联合类型,因为never和任何类型组联合类型都会过滤掉never,所以自然排除了never

就用infer硬推

题解如下:

type EffectModuleFuncName = {

[K in keyof EffectModule]: EffectModule[K] extends Function ? K : never;

}[keyof EffectModule];

type UnPackedPromise = T extends Promise ? P : T;

type EffectModuleFunc = T extends (params: infer P) => infer U

? P extends Promise

? (v: R) => UnPackedPromise

: P extends Action

? (v: X) => UnPackedPromise

: never

: never;

// 修改 Connect 的类型,让 connected 的类型变成预期的类型

type Connect = (

module: EffectModule,

) => { [K in EffectModuleFuncName]: EffectModuleFunc };

也不知道自己写的对不对,总觉得怪怪的,可以讨论一下

参考资料:

TypeScript文档

深入理解TypeScript

Previous Post
浃字的起名意思

Copyright © 2088 社攻联盟 - 大型多人在线游戏活动平台 All Rights Reserved.

友情链接