You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 11:37:04