您刚刚遇到了这个问题微软/TypeScript#27706,目前被标记为功能请求而不是错误。但这绝对令人困惑。
TypeScript 执行缩小基于一些启发式规则。其中一些规则能够缩小the unknown type,而其他人只能在以下值上正常工作联合类型. (And unknown
不是工会)。
例如,typeof类型保护器从事于unknown
,如中提到的微软/TypeScript#24439,拉取请求引入unknown
。他们在工会工作。所以这些看起来是一样的:
let value: string | number | boolean =
["abc", 123, true][Math.floor(Math.random() * 3)];
if (!(typeof value === 'string') && !(typeof value === 'number')) {
return false
}
value // string | number
and
let value: unknown =
["abc", 123, true][Math.floor(Math.random() * 3)];
if (!(typeof value === 'string') && !(typeof value === 'number')) {
return false
}
value // string | number
But 任务缩小不缩小unknown
。赋值首先将变量扩大回其原始类型(因此任何先前的缩小都将被重置),然后将变量过滤为仅可分配新值的那些联合成员。就像你正在使用the Extract<T, U>实用型.
如果你有一个类型的变量string | number | boolean
然后你分配一个string
到它,那么变量将缩小为Extract<string | number | boolean, string>
这是string
:
let value: string | number | boolean = "test";
value.toUpperCase(); // okay
But unknown
不是联合体,如果您分配一个string
对于它,结果类型将是Extract<unknown, string>
这只是unknown
,并且编译器不理解它是一个string
:
let value: unknown = 'something'
value.toUpperCase(); // error!
这就是您所看到的行为的原因。您可以使用typeof
缩小unknown
to string | number
,但是当你做作业时,它会扩大到unknown
。建议在微软/TypeScript#27706分配给unknown
变量应该缩小。如果您同意,您可能想给这个问题一个????,但这是一个相当长期的开放问题,所以我不指望这里很快会有任何改变。 (最近的缩小范围的改进似乎不影响这个。)
Playground 代码链接