代码之家  ›  专栏  ›  技术社区  ›  Justin Grant

在受约束的泛型函数中,混淆“[ts]类型…不可分配给类型[2322]错误”

  •  0
  • Justin Grant  · 技术社区  · 5 年前

    我一直在琢磨下面代码中的一个类型脚本编译器错误2322。

    function broken<A extends {a: number}>() {
      const foo: A = {a: 1};  // unexpected error: [ts] Type '{ a: number; }' is not assignable to type 'A'. [2322]
      console.log (foo);
    }
    

    如果类型是非泛型的,则类似的代码编译时不会出错。

    function works() {
      interface A {a: number};
      const foo: A = {a: 1};  // no compiler error, as expected
      console.log (foo);
    }
    

    为什么第一个函数不能编译?我想我误解了关于接口和一般约束之间的区别的一些基本的东西。

    https://codesandbox.io/s/vqx75yqx13

    1 回复  |  直到 5 年前
        1
  •  0
  •   Justin Grant    5 年前

    过了一会儿,我意识到了这个问题。将字体错误2322翻译成纯英语,意思是:“ 你试图设置 A ,具有数字属性 a 但也可能有其他属性(!!!!) ,指向仅具有数字属性的对象文本 . 由于此对象文本缺少的其他(潜在)属性,因此分配失败。

    作为问题的一个例子,可以想象用一个真正的类型替换a:

    interface A { a: number; b: string; };
    const foo: A = { a: 1 };  // compiler error, as expected
    

    如果满足泛型约束的任何可能类型(“具有数字属性”),编译器将抛出错误。 “)如果泛型类型是特定类型,则可以工作。

    理论上,在这种情况下,通过检查结果是否 foo 可能会在代码后面引起问题。例如,如果你只做一件事 是使用它的 而你不返回做任何事情 超越了它的约束,就像把它传递给其他接受 .

    但看起来typescript还没有那么聪明——它没有展望代码的未来。相反,它在分配点检查所有可能的右侧类型是否满足左侧类型的约束。如果不是,它会抛出一个错误。

    如果您确定代码不会导致问题(例如,因为您传入的值不仅仅是扩展 事实上 一个实例 )然后您可以将值强制转换为,这样分配就可以工作了。当调用外部API(如数据库)时,这是一种常见的模式,它可能返回未类型化的JSON,您可以将其强制转换为您知道的类型。这样地:

    function alsoWorks1<A extends {a: number}>() {
      const foo: A = {a: 1} as A;
      console.log (foo);
    }
    

    或者您可以决定将此函数从一般函数更改为非一般函数。这样地:

    function alsoWorks2() {
      const bar = { a: 1 };
      const foo = { a: bar.a }; // no error
      console.log (foo);
    }