typescript - 在受约束的通用函数中混淆 "[ts] Type ... is not assignable to type [2322]"错误

标签 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);
}

为什么第一个函数编译失败?我认为我对接口(interface)和通用约束之间的区别有一些基本的误解。

https://codesandbox.io/s/vqx75yqx13

最佳答案

过了一会儿,我意识到了问题所在。将 TypeScript 错误 2322 翻译成简单的英语,这意味着:“您正在尝试将 A 的值设置为具有数字属性 a 但也可能具有其他属性 (!!!) 具有对象文字只有一个数字属性 a 。由于此对象文字缺少 A 的其他(潜在)属性,因此赋值失败。”

作为问题的说明,想象用一个真实类型替换 A:

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

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

理论上,在这种情况下,TypeScript 可能会更智能,方法是检查生成的 foo 是否可能在代码的后面引起问题。例如,如果您对 foo 所做的唯一一件事是使用其 a 属性,并且您不向 foo 返回任何超出其约束的操作,例如将其传递给接受 A 的其他函数。

但似乎 TypeScript 还没有那么聪明——它没有考虑到你的代码的 future 。相反,它在赋值时检查所有可能的右侧类型是否满足左侧类型的约束。如果没有,它会抛出一个错误。

如果您确定代码不会导致问题(例如,因为您传入的值不只是扩展 A ,它实际上是 A 的实例),那么您可以将该值转换为 A 并且赋值将起作用.这是调用数据库等外部 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);
}

关于typescript - 在受约束的通用函数中混淆 "[ts] Type ... is not assignable to type [2322]"错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54610445/

相关文章:

typescript - 如何定义类型A,其中类型A可以是除类型B之外的任何类型?

typescript - 如何避免在 Typescript 中分布多个泛型类型参数?

typescript - 如何从多个属性动态构造一个类型?

typescript - TypeScript 中的柯里化(Currying)

javascript - 第一级之后无法访问 Angular CLI 中的嵌套组件

reactjs - 为什么 null React 组件状态初始化得到的是 `never` 类型?

node.js - Typescript 属性 '' 在类型 'never' 上不存在。在 React NodeJs 堆栈中

visual-studio - Visual Studio 2013 如何在没有安装 node.js 的情况下编译 TypeScript 文件?

typescript - TypeScript 中任意数量类型的非析取联合

typescript - 如何正确键入对象方法的包装函数?