为什么ngrx effect中Array.map函数内的判断条件是必须的?
问题根本原因
你的报错本质是类型不匹配和RxJS forkJoin 的参数特性两个问题共同导致的,加&&条件只是歪打正着绕开了TypeScript的严格类型校验,本身写法是有隐患的。
1. 不加条件时报错的直接原因
你调用deleteTagGroup接口之后的map操作返回的对象只有syncId一个属性,但你后续流里声明的接收类型是OperationSave[],TS类型定义里OperationSave要求必须包含success、name两个必填属性,两者类型不匹配,所以TS直接抛出2345类型错误。
2. 加&&条件后为什么不报错
&&是短路运算符,当你写tag.Id != 0 && Observable<{syncId:string}>时,map返回的数组元素类型会变成联合类型false | Observable<{syncId: string}>,这时候TypeScript的类型推断会被放宽,加上你后续强制声明接收类型为OperationSave[],相当于做了隐式类型断言,TS就不会再标红。但这种写法是有问题的:如果存在tag.Id=0的项,map会返回false到数组中,forkJoin会把false当作同步值直接返回,最终你拿到的tagIds数组里会混入false值,和你声明的OperationSave[]类型并不一致,属于类型欺骗。
3. 第二个例子中长度判断的作用
RxJS的forkJoin如果接收空数组作为参数,会直接完成但不发出任何值,你的effect流会卡在mergeMap里,永远不会触发后续的RemoveCurrentTagGroupSuccess action。你加了tagIds.length >0 && map(...)后,如果tagIds是空数组,整个表达式返回false,forkJoin会把false转为单个Observable并发出值,流就能继续往下走,所以不会报错。但同样,这种写法返回的值类型和你声明的OperationSave[]不匹配,只是绕开了校验。
正确写法
要彻底解决问题,不要用&&绕校验,按如下方式修改:
- 补全返回对象的属性,匹配
OperationSave类型要求 - 先过滤无效项再做
map,不要在map里做条件判断 - 显式处理空数组的情况
public removeCurrentTagGroup$ = createEffect(() => this.actions$.pipe( ofType(TagGroupsActions.RemoveCurrentTagGroup), withLatestFrom(this.store$.select(SelectChoosenIds)), withLatestFrom(this.store$.select(SelectTagsToDelete)), mergeMap(([[action, tagIds], tags]) => { // 先过滤掉Id=0的无效tag const validTags = tags.filter(tag => tag.Id != 0) // 空数组直接返回of([]),保证流能继续发出值 if (validTags.length === 0) return of([]) return forkJoin( validTags.map((tag) => this.phobossService.deleteTagGroup(tag.Id.toString()).pipe( // 补全OperationSave要求的所有属性 map(() => ({ syncId: tag.Id.toString(), success: true, name: 'deleteTagGroup' // 按你的业务要求填对应值 })) ) ) ) }), map((tagIds: OperationSave[]) => { return TagGroupsActions.RemoveCurrentTagGroupSuccess({ tagGroupIds: tagIds }); }) ) );
内容的提问来源于stack exchange,提问作者Bartek
相关产品推荐
相关产品推荐

