在我看来,当Typescript碰巧知道初始值时,它正在折叠一个包装的联合类型(不需要)。我不确定这是一个错误,但是我想知道是否有避免该错误的方法。
一个例子:
type Prime = 3|5|7;
type OrDonuts<T> = T | 'donuts';
function compare<T>(a: T, b: OrDonuts<T>) {
return a == b;
}
let value: Prime;
compare(value, 3);
// OK!
value = 5;
compare(value, 3);
// TS2345: Argument of type '5' is not assignable to parameter of type 'OrDonuts<3>'
为了解决此错误,我必须明确地说出value = 5 as Prime;
之类的内容。
这是错误,是预期的行为,我只是做错了吗?
(节点:10.15,打字稿:3.5.1)
答案 0 :(得分:3)
TypeScript类型检查器执行control flow type analysis,这意味着它会努力确定运行时变量内部的值会发生什么,并将缩小这些变量的类型以使其匹配。发生这种情况的一种特定方式是,如果您拥有union type的变量或属性,并且编译器看到您为其分配了更具体类型的值,它将把变量的类型缩小为更具体的类型。类型,至少直到您为变量分配了其他值为止。
这通常是理想的,因为它支持以下用例:
let x: number | string = Math.random() < 0.5 ? "hello" : "goodbye"; // x is string now
if (x.length < 6) { // no error here
x = 0; // the widest x can be is string | number, so this assignment is fine
}
请注意,即使您已经注释 x
是number | string
,但编译器仍知道在初始分配后肯定是string
。因此,当您检查x.length
时它不会抱怨(就像x
可能是number
一样)。这是一个有用的行为,禁用它会导致大量实际的TypeScript代码损坏。
不幸的是,它也对您在此处看到的行为负责。在将5
分配给value
之后,编译器将value
视为包含缩小类型5
的变量。不是Prime
。您可以始终将value
扩展到Prime
,但是编译器不会自动执行。它认为通过警告您正在呼叫compare(5 as 5, 3)
是被禁止的,这对您有帮助。
在这种情况下,如您所见,覆盖此行为的唯一方法是使用type assertion。您可以在初始分配中或在对compare()
的调用中进行以下声明:
let value2: Prime = 5 as Prime
compare(value2, 3); // okay
let value3: Prime = 5;
compare(value3 as Prime, 3); // okay
或者,您可以在对T
的调用中手动指定通用类型compare()
,这也可以:
let value4: Prime = 5;
compare<Prime>(value4, 3); // okay
您可以使用所有这些选项。
为此,我能找到的最规范的文档来源是Microsoft/TypeScript#8513,尤其是this comment。
好的,希望能有所帮助;祝你好运!
答案 1 :(得分:1)
这是Union Types的预期行为。通过声明
type Prime = 3|5|7; // it is either 3 OR 5 OR 7 but never all of them at the the same time
您告诉ts编译器Prime
是值之一。现在,您将5
分配给键入字段value
。
value = 5; // type Prime is now 5
compare(value, 3); // <T> is inferred by the ts-compiler as 5
要解决此问题,您必须使用value:Prime
或像以前一样键入assert。
无需类型推断,您就可以通过value
,例如
value = 5;
compare<2>(value, 3); // Argument of type '5' is not assignable to parameter of type '2'.
或者您是否愿意以更通用的方式满足compare
函数参数
let test = 5;
compare(test, 3); // <T> is now number which 5 and 3 are.