为何TypeScript返回含不可能可选键的判别式联合?
问题原因
TypeScript推断返回多个对象的函数类型时,默认会统一所有返回值的结构:它会把所有返回对象的键都收集起来,给每个对象里不存在的键加上?: undefined标记,确保联合类型的每个成员都能兼容一个包含所有键的基础结构。这就是你看到额外可选键的原因。
你第二个例子能得到纯净联合类型,是因为两个返回值(对象和数组)结构完全不兼容,TypeScript没法统一它们的结构,所以直接保留了各自的原始类型。
不用显式定义返回类型的解决办法
办法1:用never标记不存在的键
在每个返回对象里,给另一个分支的键赋值为undefined as never——never类型和任何类型都不兼容,TypeScript就不会再尝试统一结构:
export const testFn = () => { if (Math.random() > 0.5) return { valid: true, data: { your: 1 }, error: undefined as never } as const; return { valid: false, error: { my: "error" }, data: undefined as never } as const; }; type T1 = ReturnType<typeof testFn> // 最终类型: // { // readonly valid: true; // readonly data: { // readonly your: 1; // }; // readonly error: never; // } | { // readonly valid: false; // readonly error: { // readonly my: "error"; // }; // readonly data: never; // }
实际使用时完全不用担心never字段:用valid判别后,TypeScript会自动忽略不存在的字段,比如:
const res = testFn(); if (res.valid) { console.log(res.data.your); // 正常访问,不会提示error存在 } else { console.log(res.error.my); // 正常访问,不会提示data存在 }
办法2:人为制造结构差异
给其中一个返回对象加一个唯一的符号键,让TypeScript认为两个返回值结构不同,从而停止合并键:
const tempKey = Symbol(); export const testFn = () => { if (Math.random() > 0.5) return { valid: true, data: { your: 1 }, [tempKey]: true } as const; return { valid: false, error: { my: "error" } } as const; }; type T1 = ReturnType<typeof testFn> // 最终类型: // { // readonly valid: true; // readonly data: { // readonly your: 1; // }; // readonly [tempKey]: true; // } | { // readonly valid: false; // readonly error: { // readonly my: "error"; // }; // }
这个办法会多一个无用的符号键,但符号键不会被意外访问,也不影响判别式联合的正常使用。
总结
不想显式定义返回类型的话,核心就是让TypeScript没法统一两个返回对象的结构——要么用never标记不该存在的键,要么人为制造结构差异,阻止它自动合并键。
内容的提问来源于stack exchange,提问作者moroncodes
相关产品推荐
相关产品推荐

