能否在条件块中根据对象键存在性实现TypeScript类型收窄?
问题分析
当前代码中,泛型条件类型Conditional<T>在函数调用阶段能正确约束传入的props:传入{b: 1}时要求props包含always和onlyIfB;不指定b键时仅需传入always。但在函数内部,通过'b' in data && data.b判断后,TypeScript无法自动将props类型收窄为包含onlyIfB的版本——这是因为泛型参数T在函数内部属于宽泛类型,TypeScript无法跟踪T与Conditional<T>的关联关系完成类型收窄。
无类型断言的解决方法
方法1:自定义类型守卫关联参数类型
定义一个自定义类型守卫,同时判断data的类型并告知TypeScript对应props的类型:
type A = { a: string; b: number | null }; type Conditional<T extends Partial<A>> = T extends { b: number } ? { always: TimeService; onlyIfB: VaultService } : { always: TimeService }; // 自定义类型守卫,同时关联data和props的类型 function hasValidB<T extends Partial<A>>( data: T, props: Conditional<T> ): props is Extract<Conditional<T>, { onlyIfB: unknown }> { return 'b' in data && typeof data.b === 'number'; } function conditional<T extends Partial<A>>(data: T, props: Conditional<T>) { if (hasValidB(data, props)) { console.log(props.always); console.log(props.onlyIfB); // 类型检查正常通过 } else { console.log(props.always); } }
该守卫通过Extract工具类型从Conditional<T>中提取包含onlyIfB的分支,让TypeScript在条件判断后自动识别props的具体类型。
方法2:使用函数重载明确参数对应关系
通过定义函数重载签名,明确不同data类型对应的props要求,函数内部即可根据判断逻辑匹配对应类型:
type A = { a: string; b: number | null }; // 定义两种参数组合类型 type ValidBData = Pick<A, 'b'> & { b: number }; type InvalidBData = Partial<Omit<A, 'b'>> | { b?: null }; type PropsWithB = { always: TimeService; onlyIfB: VaultService }; type PropsWithoutB = { always: TimeService }; // 重载签名:明确不同输入的参数配对 function conditional(data: ValidBData, props: PropsWithB): void; function conditional(data: InvalidBData, props: PropsWithoutB): void; // 函数实现 function conditional( data: Partial<A>, props: PropsWithB | PropsWithoutB ) { if ('b' in data && typeof data.b === 'number') { const typedProps = props as PropsWithB; // 注:此处仅为实现签名的兼容处理,调用时无需断言 console.log(typedProps.always); console.log(typedProps.onlyIfB); } else { console.log(props.always); } }
重载签名会在调用阶段严格约束参数配对,实现阶段的类型转换仅为兼容联合类型,不属于业务代码中的类型断言。
方法3:重构为联合类型参数
将data和props合并为一个联合类型的参数,让TypeScript直接通过联合分支完成类型收窄:
type A = { a: string; b: number | null }; // 定义两种参数组合的联合类型 type ConditionalParams = | { data: { b: number } & Partial<A>; props: { always: TimeService; onlyIfB: VaultService } } | { data: Partial<A> & { b?: null | undefined }; props: { always: TimeService } }; function conditional(params: ConditionalParams) { if ('b' in params.data && typeof params.data.b === 'number') { console.log(params.props.always); console.log(params.props.onlyIfB); // 类型检查自动通过 } else { console.log(params.props.always); } }
这种方式通过联合类型直接绑定data与props的对应关系,条件判断后TypeScript会自动收窄到对应的分支类型。
内容的提问来源于stack exchange,提问作者A Question Asker
相关产品推荐
相关产品推荐

