首页 > 解决方案 > 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]

看来打字稿在 和 中对返回对象的属性test进行了不同的分类。在中,它知道该值为 a 。但在 中,它只将其归类为字符串,导致类型错误。test1test2test1'a'TestSymboltest2

为什么它会这样做,我如何帮助 typescript 理解它testTestSymbolin test2

标签: typescript

解决方案


当变量或其他表达式上没有显式类型注释时,TypeScript 编译器将根据一些启发式规则推断其类型,这些规则是对预期类型的​​合理猜测。这些规则并不完美,但它们很有用。

在你的第一个例子中,

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

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

例如,当您声明如下变量时:

const x = { test: 'a' };

x编译器会立即根据 value推断出 的类型{test: 'a'}。它不会等到它x在某处看到使用时才知道最好的类型是什么。推断的类型是{test: string}。即使x是 a const,该属性x.test也可以更改,因此编译器假定它x.test可以是 any string。通常这样的假设是正确的,但在你的情况下它不是。


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

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

或者您可以使用const断言来更改推理启发式,以便编译器假定任何内容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的事实有点奇怪,但这是预期的行为,您可以在microsoft/TypeScript#13347阅读更改此设置的建议)并且没有编译器错误。readonlyreadonly

无论哪种方式都应该有效。


请注意,我不建议使用以下形式的类型断言

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

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

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为 a ,因此注释或断言更安全。TestTypeconst


Playground 代码链接


推荐阅读