NgRx中使用自定义RxJS运算符触发RangeError:调用栈溢出
问题原因分析
这个栈溢出错误大概率是自定义运算符的类型处理与NgRx v16的类型系统不兼容导致的无限递归。NgRx从v12升级到v16后,类型检查变得更严格,尤其是对Effects中action流的类型推断逻辑。
你的自定义运算符存在两个核心问题:
- 多余的
map类型断言:ofType(SharedActions.poll)已经能正确推断出action的类型为ReturnType<typeof SharedActions.poll>,额外的map+强制类型断言会干扰TypeScript的类型推断,触发NgRx内部的类型转换循环。 - 类型定义不严谨:使用
UnaryFunction而非NgRx更适配的OperatorFunction,或简化版中错误的类型断言,导致NgRx无法正确识别运算符返回的流类型,进而引发无限订阅/类型递归。
解决办法
方案1:正确利用ofType的类型推断(推荐)
重构自定义运算符,移除多余的类型断言,让ofType和filter自然推导类型:
import { pipe } from 'rxjs'; import { ofType, OperatorFunction } from '@ngrx/effects'; import { filter } from 'rxjs/operators'; import { Action } from '@ngrx/store'; import { SharedActions } from './shared.actions'; /** * Filters actions of type SharedActions.poll and returns only those that match the given poll id. */ export const isPollAction = ( pollId: string ): OperatorFunction<Action, ReturnType<typeof SharedActions.poll>> => { return pipe( ofType(SharedActions.poll), filter((action) => action.pollId === pollId) ); };
方案2:使用类型守卫的简化版
如果不想依赖ofType,可以用类型守卫让TypeScript正确识别action类型,避免错误的类型断言:
import { OperatorFunction } from 'rxjs'; import { filter } from 'rxjs/operators'; import { Action } from '@ngrx/store'; import { SharedActions } from './shared.actions'; export const isPollAction = ( pollId: string ): OperatorFunction<Action, ReturnType<typeof SharedActions.poll>> => { return filter((action): action is ReturnType<typeof SharedActions.poll> => { return action.type === SharedActions.poll.type && action.pollId === pollId; }); };
验证使用方式
替换自定义运算符后,Effects中的使用无需额外类型处理:
pollTriggered$ = createEffect(() => this.actions$.pipe( isPollAction('myPollId'), mergeMap(() => { // 此处action会自动推断为正确类型,无需手动断言 // ... 业务逻辑代码 }) ) );
关键说明
- NgRx v14+对
ofType的类型系统做了大幅优化,不再需要手动通过map转换action类型。 - 类型守卫(
action is ...)比强制类型断言更安全,能让TypeScript和NgRx正确识别流的类型,从根源避免类型相关的递归错误。
内容的提问来源于stack exchange,提问作者Riku Amon
相关产品推荐
相关产品推荐

