TypeScript联合类型条件分支中类型推断异常问题咨询
解决TypeScript中根据multiple值推断active和onChange类型的问题
问题场景
我定义了一个IconSelectorProps类型,通过multiple字段区分单选/多选模式,自动切换active和onChange的类型:
export type IconSelectorProps = { className?: string; name: string; items: SelectorItem[]; } & (MultipleSelector | SingleSelector); type SingleSelector = { multiple: false } & Selector<SelectorItem>; type MultipleSelector = { multiple: true } & Selector<SelectorItem[]>; interface Selector<T> { active: T; onChange: (item: T) => void; } export interface SelectorItem { icon: string; value: string; title?: string; }
但编写setActiveItem函数时,else if分支出现类型错误:
const setActiveItem = (item: SelectorItem) => { if (multiple && active.some((e) => e.value == item.value)) { onChange([...active, item]); } else if (active.value !== item.value) { onChange(item); } };
错误信息
Property 'value' does not exist on type 'SelectorItem | SelectorItem[]'. Property 'value' does not exist on type 'SelectorItem[]'.
Argument of type 'SelectorItem' is not assignable to parameter of type 'SelectorItem[] & SelectorItem'. Type 'SelectorItem' is missing the following properties from type 'SelectorItem[]': length, pop, push, concat, and 29 more.
问题原因
TypeScript无法在else if分支中正确缩小类型范围:
- 第一个if分支仅覆盖了
multiple为true且active包含目标item的场景 - else if分支的条件
active.value !== item.value尝试访问active.value,但此时active的类型仍为SelectorItem | SelectorItem[](因为else分支还包含multiple为true但active不包含目标item的情况,此时active是数组,没有value属性) - 同时
onChange的类型被推断为两种函数类型的交集,只有同时满足SelectorItem和SelectorItem[]的参数才能传入,导致传参报错
解决方案
将单选、多选逻辑拆分为互斥分支,让TypeScript能明确推断每个分支的类型:
const setActiveItem = (item: SelectorItem) => { // 单选模式:multiple为false时,active是单个SelectorItem if (!multiple) { if (active.value !== item.value) { onChange(item); } return; } // 多选模式:multiple为true时,active是SelectorItem数组 const isSelected = active.some(e => e.value === item.value); if (isSelected) { // 原逻辑:已选中则添加(可根据需求改为移除) onChange([...active, item]); } else { // 未选中则添加(可根据需求调整逻辑) onChange([...active, item]); } };
如果需要保留原有的分支结构,也可以使用类型守卫明确缩小类型:
// 定义类型守卫函数 function isSingleMode(multiple: boolean, active: SelectorItem | SelectorItem[]): active is SelectorItem { return !multiple; } const setActiveItem = (item: SelectorItem) => { if (multiple && active.some((e) => e.value == item.value)) { onChange([...active, item]); } else if (isSingleMode(multiple, active) && active.value !== item.value) { onChange(item); } };
内容的提问来源于stack exchange,提问作者dzhd
相关产品推荐
相关产品推荐

