受约束的泛型函数中令人困惑的“ [ts]类型...无法分配给类型[2322]”错误

时间:2019-02-09 20:31:07

标签: typescript typescript-generics

我一直在困惑以下代码中的TypeScript编译器错误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 个答案:

答案 0 :(得分:0)

一段时间后,我意识到了问题所在。将TypeScript错误2322转换为简单的英语,这意味着:“ 您正在尝试设置A的值,该值具有数字属性a ,但可能还具有其他属性(!!!),到仅具有数字属性a的对象文字。由于该对象文字缺少A的其他(潜在)属性,分配失败。“ < / p>

为说明问题,想象一下用实型替换A:

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

如果通用类型是特定类型,则满足通用约束的任何可能类型(“具有数字属性a”)都可以工作时,编译器将引发错误。

理论上,通过检查生成的foo是否可能在以后的代码中引起问题,在这种情况下TypeScript可能会更聪明。例如,如果您对foo所做的唯一事情就是使用其a属性,而您没有返回对foo进行超出其约束范围的任何操作,例如将其传递给接受A的其他函数。

但是TypeScript似乎还不那么聪明-它没有考虑到代码的未来。而是在分配时检查是否所有可能的右侧类型都满足左侧类型的约束。如果不是,则抛出错误。

如果您确定代码不会引起问题(例如,因为您传入的值不只是扩展A,那么它实际上 是{ {1}}),然后可以将值强制转换为A,这样分配就可以了。这是调用外部API(例如数据库)时的一种常见模式,该API可能会返回无类型的JSON,您可以将该类型转换为您知道的类型。像这样:

A

或者您可以决定将其从通用函数更改为非通用函数。像这样:

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