我对下面的代码感到困惑:
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 在 test1
和 test2
中对返回对象的 test
属性进行了不同的分类。在 test1
中,它理解 'a'
值是一个 TestSymbol
。但是在test2
中,它只是将其归类为字符串,导致类型错误。
为什么要这样做,我如何帮助 typescript 理解 test
是 test2
中的 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
断言更安全。
关于Typescript 根据是否存在变量更改返回值的类型推断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68422349/