调用联合返回类型函数时,条件运算符类型推断异常问题
TypeScript联合类型在条件/逻辑运算符中的类型推断问题
在TypeScript中,当通过条件运算符(三元)或逻辑运算符调用返回联合类型的函数时,会出现类型推断丢失部分联合成员的问题。比如以下代码中,thing、thing2、thing3、thing4的类型被推断为Row | "hi",但实际应该包含RowAndColumn:
type Row = { row: number }; type RowAndColumn = { row: number, column: number } function getThing(input: number): RowAndColumn | Row | null { if (input < 0.3) return null; if (input < 0.5) return { row: 0 }; return { row: 0, column: 0 }; } const rand = Math.random(); const input = Math.random(); const thing = (rand > 0.5 && getThing(input)) || 'hi'; // 类型缺失RowAndColumn const thing2 = (rand > 0.5 && getThing(input)) ?? 'hi'; // 类型缺失RowAndColumn const thing3 = rand > 0.5 ? getThing(input) : 'hi'; // 类型缺失RowAndColumn const thing4 = rand > 0.5 ? getThing(input) : getThing(input); // 类型缺失RowAndColumn const correct = getThing(input); // 类型推断正确:RowAndColumn | Row | null const correct2 = rand > 0.5 && getThing(input); // 类型推断正确:RowAndColumn | Row | null
问题原因
这是TypeScript的严格子类型收缩行为导致的:
RowAndColumn是Row的结构子类型,前者完全包含后者的所有属性。- 在逻辑运算符(
&&/||/??)或三元运算符的类型计算中,TypeScript会自动将子类型合并到父类型中,只保留更宽泛的父类型(即Row),以此简化联合类型的复杂度。 - 而直接调用函数或仅使用
&&与函数返回值结合时,没有和其他异质类型(比如字符串"hi")进行联合,因此不会触发这种子类型合并,能保留完整的联合成员。
解决办法
如果需要保留完整的联合类型,有两种常见方案:
- 手动指定类型:显式声明变量的完整类型:
const thing: Row | RowAndColumn | "hi" = (rand > 0.5 && getThing(input)) || 'hi';
- 使用可区分联合:给类型添加唯一的区分标志,打破子父类型关系:
type Row = { type: 'row', row: number }; type RowAndColumn = { type: 'rowAndColumn', row: number, column: number } function getThing(input: number): RowAndColumn | Row | null { if (input < 0.3) return null; if (input < 0.5) return { type: 'row', row: 0 }; return { type: 'rowAndColumn', row: 0, column: 0 }; }
此时TypeScript会保留所有联合成员,不会进行子类型合并。
内容的提问来源于stack exchange,提问作者Jeremy Bernier
相关产品推荐
相关产品推荐

