为何TypeScript中`{ foo: never }`无法赋值给`never`类型?
为什么TypeScript将
{ foo: never }与never视为不同类型? 核心原因:类型构造器与结构语义的差异
TypeScript的类型系统中,never和包含never字段的对象类型属于完全不同的类型范畴:
never是底层空类型,代表不存在任何可能的值,没有任何值(包括对象)能被赋值给never类型。{ foo: never }是对象结构类型,它描述了一个必须包含foo属性的对象,且该属性的值属于never类型。虽然这个对象类型也没有可被实例化的有效值(因为foo属性无法被赋值),但TypeScript的类型检查器不会将其等同于never——因为两者的类型构造逻辑不同:一个是原生空类型,一个是带有属性约束的对象类型,结构语义上存在本质区别。
这也是为什么你会看到这个报错:
(x: { foo: never }): never => x; // 错误: Type '{ foo: never; }' is not assignable to type 'never'.
{ foo: never }是对象类型,而never是底层空类型,两者不满足赋值兼容性。
解决你的联合类型简化问题
你遇到的Transform类型输出未简化的问题,本质是TypeScript只会自动移除联合中的never成员,而不会自动将含never字段的对象类型转为never。要实现预期的简化效果,可以在类型转换中添加判断:当对象的所有字段都是never时,将整个对象类型转为never,这样就能被联合自动过滤。
修改后的代码如下:
type ExtractFoo<T> = T extends { a: infer A; b: infer B } ? [A, B] : never; type ExtractBar<T> = T extends { c: infer C; d: infer D } ? [C, D] : never; // 判断类型是否为never type IsNever<T> = [T] extends [never] ? true : false; type Transform<T> = T extends unknown ? // 如果foo和bar都是never,就转为never IsNever<ExtractFoo<T>> & IsNever<ExtractBar<T>> extends true ? never : { foo: ExtractFoo<T>; bar: ExtractBar<T> } : never; type T = Transform<{ a: 1; b: 2; c: 3; d: 4 } | { a: 'a'; b: 'b' } | { a: true; c: false }>; // 输出: { foo: [1, 2]; bar: [3, 4] } | { foo: ["a", "b"]; bar: never }
如果需要进一步移除单个字段为never的对象(比如{ foo: ["a", "b"]; bar: never }),可以调整判断逻辑,只要任意一个字段是never就转为never:
type Transform<T> = T extends unknown ? IsNever<ExtractFoo<T>> | IsNever<ExtractBar<T>> extends true ? never : { foo: ExtractFoo<T>; bar: ExtractBar<T> } : never;
这样就能得到你预期的仅保留{ foo: [1, 2]; bar: [3, 4] }的结果。
内容的提问来源于stack exchange,提问作者vijrox
相关产品推荐
相关产品推荐

