TypeScript函数与类型交叉理解及NgRx ActionCreator实例化报错问题
TS 函数与对象交叉类型的本质
TypeScript 的交叉类型A & B的核心规则是同时满足A和B的所有结构要求,不存在天然冲突:
- JS 中函数本身就是可挂载自定义属性的一等对象,因此TS允许把函数类型和对象类型做交叉
- 形如
(...args: P) => R & { type: string }的交叉类型,对值的要求有两点:- 值本身是可调用的:接收匹配
P的参数列表,调用后返回R类型的结果 - 值自身(注意是函数对象本身,不是调用后的返回值)存在
type属性,属性值为string类型
- 值本身是可调用的:接收匹配
举个最小的合法示例:
// 纯函数定义,满足可调用要求 const sampleFn = (name: string) => ({ hello: name }); // 给函数对象自身挂载type属性,满足对象结构要求 sampleFn.type = 'greet-action';
上面的sampleFn就完全符合(name: string) => { hello: string } & { type: string }的交叉类型要求,没有任何语法上的障碍。
原代码的报错原因
原代码的问题完全出在两处结构不匹配:
- 第一处:你声明的Creator泛型是
() => ({ b: number, type: string }),即无参函数,但赋值右侧的写法([]) => ({...}),会被TS推断为“接收1个Iterable<any>类型参数的函数”,带参数的函数无法兼容赋值给无参函数类型(TS的函数参数兼容遵循逆变规则,无参类型要求被赋值的函数不能声明必填的前置参数) - 第二处:你目前把
type属性写在了函数的返回值对象里,这和交叉类型要求的“函数对象自身挂载type属性”是完全不同的结构,就算把参数问题修好,依然不满足TypedAction的结构要求。
手动构造ActionCreator的正确方式
构造时需要完全对齐两个结构要求:
- 写一个和Creator泛型签名完全一致的函数,不要多写形参
- 给这个函数对象本身显式挂载
type属性,属性值匹配泛型要求
可运行的代码示例:
// 1. 写和Creator签名完全匹配的无参函数 const rawCreator = () => ({ b: 1 }); // 2. 给函数自身挂载type属性,满足TypedAction结构 (rawCreator as any).type = 'abc'; // 可正常赋值,无类型报错 const ac: ActionCreator<string, () => ({ b: number })> = rawCreator;
补充说明:
- 常见认知误区:ActionCreator的交叉类型中,
TypedAction<T>是叠加在Creator(函数)本身上的,因此不需要要求Creator的返回值R里包含type字段,type字段的归属是函数实例,不是函数的返回值- NgRx提供的
createAction本质上就是封装了上面的手动逻辑——接收你传入的payload构造函数,给生成的函数自动挂上对应的type字符串属性,最后把结果类型转成对应的交叉类型,没有额外的魔法逻辑
内容的提问来源于stack exchange,提问作者Wolf359
相关产品推荐
相关产品推荐

