Typescript 根据是否存在变量更改返回值的类型推断

标签 typescript

我对下面的代码感到困惑:

type TestSymbol = 'a' | 'b';
type TestType = { test: TestSymbol }

function test1(): TestType {  // no error
  return { test: 'a' };
}

function test2(): TestType {  // tsserver error 2322
  const x = { test: 'a' };
  return x;
}

错误信息是:

Type '{ test: string; }' is not assignable to type 'TestType'.
  Types of property 'test' are incompatible.
    Type 'string' is not assignable to type 'TestSymbol'.
[tsserver: 2322]

似乎 typescript 在 test1test2 中对返回对象的 test 属性进行了不同的分类。在 test1 中,它理解 'a' 值是一个 TestSymbol。但是在test2中,它只是将其归类为字符串,导致类型错误。

为什么要这样做,我如何帮助 typescript 理解 testtest2 中的 TestSymbol

最佳答案

当没有明确的type annotation时在变量或其他表达式上,TypeScript 编译器将 infer它的类型基于一些启发式规则,这些规则是对预期类型的​​合理猜测。这些规则并不完美,但很有用。

在你的第一个例子中,

function test1(): TestType {  
  return { test: 'a' };
}

编译器将{test: 'a'} 的类型推断为TestType contextually通过函数的返回类型。上下文类型推断意味着编译器根据预期存在的类型推断表达式的类型。这只会在特定情况下发生,如果已经为值推断出类型,则不会发生。

例如,当您声明一个变量时:

const x = { test: 'a' };

编译器根据值{test: 'a'} 立即推断出x 的类型。它不会等到它看到 x 被用在某处时才找出最好的类型。推断类型为 {test: string}。尽管 x 是一个 const,属性 x.test 可以改变,所以编译器假定 x.test 可以是任何字符串。通常这样的假设是正确的,但在您的情况下却不是。


要解决此问题,您可以显式注释 x 的类型:

function testAnnotation(): TestType {
  const x: TestType = { test: 'a' }; // <-- explicit annotation
  return x; // okay
}

或者您可以使用 const assertion更改推理启发式,以便编译器假定关于 x 的任何内容都不会更改:

function testConstAssertion(): TestType {
  const x = { test: 'a' } as const; // <-- ask for narrowest possible type
  // const x: { readonly test: "a"; } 
  return x; // okay
}

x 的推断类型现在是 { readonly test: "a"; ;编译器假设 test 属性永远不会改变,并且它唯一可能的值是 "a"。这被视为可分配给 TestType(readonly 属性可分配给非 readonly 属性这一事实有点奇怪,但这是预期的行为您可以在 microsoft/TypeScript#13347 阅读更改此设置的建议)并且没有编译器错误。

任何一种方式都应该有效。


请注意,我不建议使用 type assertion形式:

function testLessSafeTypeAssertion(): TestType {
  const x = { test: 'a' } as TestType;
  return x;
}

虽然这会编译,但不太安全,因为您只是告诉编译器 {test: "a"} 是一个 TestType 而不是要求编译器验证它。类型断言往往会遗漏某些错误:

const x = { test: Math.random() < 0.5 ? 'a' : 'z' } as TestType; // no error!

类型注解捕捉它们的地方:

const x: TestType = { test: Math.random() < 0.5 ? 'a' : 'z' }; // error

类型断言主要用于编译器无法验证值类型的情况,而您需要向编译器提供它没有的信息。在这种情况下,编译器可以自行检查 x 是否为 TestType,因此注释或 const 断言更安全。


Playground link to code

关于Typescript 根据是否存在变量更改返回值的类型推断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68422349/

相关文章:

angular - Angular 6 中 ngIf 的多个条件

dependency-injection - 带有可选参数的 DI 构造函数

Angular 7、Ngrx、Rxjs 6 - 在延迟加载的模块之间访问状态

Node.js 和sequelize-typescript - 数据访问对象和业务对象

javascript - Angular2 解析 Promise 时出错 : Type 'Promise<Hero[]>' is no t assignable to type 'Hero[]'

typescript - Ionic 2 - 如何根据先前的下拉选择显示第二个下拉数据

javascript - React - 使用map()时设置默认属性

javascript - eval() 之后无法访问函数

typescript - 解释 TypeScript 中的 "export ="和 "export as namespace"语法

javascript - 使用 angular 和 css 从数组数组创建自定义网格