代码之家  ›  专栏  ›  技术社区  ›  Sanitysign

为什么{}类型可分配给对象类型?

  •  3
  • Sanitysign  · 技术社区  · 5 月前

    有人能解释一下原因吗 Second 下面的类型是 true ?

    type First = object extends {} ? true : false // true
    type Second = {} extends object ? true : false // true
    

    知道这一点 {} 除了 null undefined 虽然 object 是任何非原始类型,我理解为什么 First 类型是 真的 但我不明白为什么 第二 类型也是 真的

    1 回复  |  直到 5 月前
        1
  •  3
  •   jcalz    5 月前

    你说得对 {} extends object 应该是假的,因为不是每一个 {} 是一个 object . Primitives 喜欢 string number boolean 可分配给 {} (接受任何非无效值),但不接受 对象 (拒绝基元)。那么,为什么允许这样做呢?

    权威答案包含在 a comment on microsoft/TypeScript#56205 TS团队开发负责人:

    允许 {} 用作 对象 这是一个有意的兼容性漏洞,因为 对象 并不总是存在的,所以人们用 {} 作为下一个最好的事情。

    所以这是故意的 unsound 为了不破坏现实世界中大量代码的存在 对象 .

    更多详细信息可以在 microsoft/TypeScript#60582 .如果 {} 不可转让给 对象 那么,两者都不会 {a: string} 然后是接受的东西 对象 s会突然 拒绝 大多数类型和接口,因为 也许 吧 它们很原始吗?(尽管 {a:string} 不可能是原始的。)因此,TypeScript可能需要积极分析每个接口,看看它是否与原语重叠,然后允许那些重叠的接口 重叠部分可分配给 对象 ,所以 {length: number, a: string} 可分配给 对象 但是 {length: number} 不是吗?正如前文所述 another comment TS团队开发负责人:

    根本问题是,这里的不一致性不可能 远离的 ,他们可以只是 移动 我们说这个项目是合法的

    function foo(s: string) {
       return s.length;
    }
    

    但是 为什么? 这合法吗?这是合法的,因为 一串 类型还包括来自全局的属性 String 类型。

    同样,出于同样的原因,我们允许您编写此程序:

    function foo<T extends { length: number }>(x: T) {
      return x.length;
    }
    // Legal call
    foo("hello world");
    

    这里的建议意味着打破了这种一致性:如果某些表达式涉及对基元的属性访问,则不能再使用某些表达式块并以更高阶的方式讨论它们。访问 length 一个字符串现在是一个 根本不同 操作比访问 长度 一个数组。

    这引发了更多的问题,考虑一下这样的问题

    const p = "foo";
    type X = (typeof p)["length"];
    
    type GetLength<T> = T extends { length: infer U } : U ? never;
    type Y = GetLength<typeof p>;
    

    是类型 X 甚至合法?一个论点是肯定的,因为如果你写作,你会得到这样的结果 p.length .另一种说法是否定的,因为这基本上相当于 GetLength ,现在将生产 never 相反。

    我们生成或摄取属性名称的每个地方,现在都必须考虑该属性名称是否作为对象语法的一部分存在。例如,什么是 keyof string ?

    这也隐含地打破了常见的“品牌”模式,如 string & { isNormalized: true } 自从 string & object 很明显 从未 .

    我也会权衡一下这一点 interface 隐式扩展 对象 但不是 type X = { } 看起来非常不一致,可以说是违反直觉的?如果有的话,我会选择相反的。你可以想象让写作合法化 interface Foo extends object { (并在你的代码库中使其习惯化),但必须插入一个 & object 进入a type 宣言。但无论哪种方式,它都引入了“你必须知道的另一件事”,从保持语言可学习性的角度来看,这并不好。

    所以问题是周围有一种奇怪的不健康 {} 对象 ,而且,在不引入其他更奇怪、更不健全的情况下,所有提出的处理方法都没有奏效。