TypeScript联合类型非公共属性访问报错,寻求解决方法
解决TypeScript类型推断导致的属性访问错误
问题背景
定义了三个共享predicate属性的TypeScript类型:ActivityRouting、ActivityStepOptionsOverride、ActivityStepContentSource,其余属性各不相同。编写了processPredicates函数处理这三种类型的数组,返回符合条件且去除predicate的对象:
private async processPredicates( routing: ActivityRouting[] | ActivityStepOptionsOverride[] | ActivityStepContentSource[] ) { for (const { predicate, ...block } of routing) { if (predicate == null) return block; else if (await this.callMethod(PREDICATES, predicate)) return block; } return null; }
调用该函数后,尝试访问返回结果的sideEffects和next属性时,出现TS2339错误:
Property 'sideEffects' does not exist on type '{ sideEffects?: SideEffect[] | undefined; next?: Next | undefined; } | { reduce?: string | undefined; use?: string | undefined; } | { mode?: string | undefined; }'. Property 'sideEffects' does not exist on type '{ reduce?: string | undefined; use?: string | undefined; }'.ts(2339)
Property 'next' does not exist on type '{ sideEffects?: SideEffect[] | undefined; next?: Next | undefined; } | { reduce?: string | undefined; use?: string | undefined; } | { mode?: string | undefined; }'. Property 'next' does not exist on type '{ reduce?: string | undefined; use?: string | undefined; }'.ts(2339)
错误原因
TypeScript无法推断返回值的具体类型:函数参数是三种数组的联合类型,返回值对应三种类型去掉predicate后的联合类型。访问仅存在于某一种类型上的属性时,TS无法确认该属性在当前值上是否存在,因此抛出错误。
解决方案
方法1:泛型约束匹配输入输出类型
将processPredicates改为泛型函数,约束输入类型包含predicate属性,让TS能根据输入数组的类型推断出精确的返回类型:
private async processPredicates<T extends { predicate?: unknown }>( routing: T[] ): Promise<Omit<T, 'predicate'> | null> { for (const { predicate, ...block } of routing) { if (predicate == null) return block; else if (await this.callMethod(PREDICATES, predicate)) return block; } return null; }
调用时,传入ActivityRouting[],返回值会被推断为Omit<ActivityRouting, 'predicate'> | null,此时可以安全访问sideEffects和next属性。
方法2:类型断言(快速但需谨慎)
如果能明确当前场景下返回的是目标类型对应的结果,可使用类型断言强制指定类型:
const leftoverRouting = await this.processPredicates(routingArray); if (leftoverRouting) { const routing = leftoverRouting as Omit<ActivityRouting, 'predicate'>; console.log(routing.sideEffects, routing.next); }
注意:这种方法需要开发者自行保证类型正确性,否则可能引发运行时错误。
方法3:类型守卫缩小类型范围
定义类型守卫函数,判断返回值是否为目标类型,让TS在守卫分支中正确推断类型:
function isActivityRoutingResult( obj: unknown ): obj is Omit<ActivityRouting, 'predicate'> { return typeof obj === 'object' && obj !== null && 'sideEffects' in obj && 'next' in obj; }
调用时通过类型守卫缩小范围:
const leftoverRouting = await this.processPredicates(routingArray); if (isActivityRoutingResult(leftoverRouting)) { console.log(leftoverRouting.sideEffects, leftoverRouting.next); }
这种方法更安全,TS会在守卫分支中确认属性存在性。
内容的提问来源于stack exchange,提问作者Onyx
相关产品推荐
相关产品推荐

