TypeScript中为何用[M] extends [never]包裹泛型?Redux Toolkit解析
为什么Redux Toolkit的PayloadAction要用元组包裹泛型做extends判断?
先看Redux Toolkit中PayloadAction的源码:
export type PayloadAction< P = void, T extends string = string, M = never, E = never > = { payload: P type: T } & ([M] extends [never] ? {} : { meta: M }) & ([E] extends [never] ? {} : { error: E })
核心原因:规避TypeScript分布式条件类型对never的特殊处理
直接用M extends never会触发分布式条件类型,而never作为空联合类型,会导致整个条件判断的结果不符合预期:
- 分布式条件类型的特性:当条件类型的左侧是裸泛型参数(没有被包裹在元组、对象等结构里),且该参数是联合类型时,TypeScript会把条件逻辑分别应用到联合类型的每个成员上,再把结果合并成新的联合类型。
- never的特殊表现:
never是没有任何成员的联合类型,当裸泛型参数M等于never时,M extends never ? A : B会因为没有可遍历的成员,最终返回never,而不是我们想要的A(即{})。
具体后果(你的错误示例)
当你把判断改成M extends never后:
export type PayloadAction< P = void, T extends string = string, M = never, E = never > = { payload: P; type: T; } & ( M extends never ? {} : { meta: M; }) & ([E] extends [never] ? {} : { error: E; });
在示例中传入M=never时,M extends never的结果是never,原对象类型{payload: void, type: 'FAILURE'}和never交叉后,整个PayloadAction类型会变成never——这就是为什么你创建的errorAction会提示“无法赋值给类型never”。
元组包裹的作用
用[M] extends [never]时,[M]是一个元组类型,不是裸泛型参数,因此不会触发分布式条件类型。此时判断的是“由M组成的元组是否等于由never组成的元组”:
- 如果
M=never,则[never] extends [never]为true,返回{},和原对象交叉后不改变类型; - 如果
M是其他类型(比如string),则[string] extends [never]为false,返回{meta: M},从而为Action添加meta属性。
同理,[E] extends [never]也是为了正确判断E是否为never,避免同样的问题。
内容的提问来源于stack exchange,提问作者talpx
相关产品推荐
相关产品推荐

