type Test = 'a' | 'b' | 'c';
function foo<T extends Test>(arg: T) {
if (arg === 'a') {
console.log(arg);
}
}
喜欢这个。。我希望 arg 推断出 if 块中的"a"。
但 ts 推断为只是 T。
为什么会出现这种情况?
我认为extends
关键字有一些东西...
问题是你的类型T
不是Test
而是子集,我们可以说每个可以使用的类型代替T
,每个 for union 意味着具有相同或更少工会成员的类型。考虑扩展Test
的示例类型:
type Test = 'a' | 'b' | 'c';
type SubTest = 'b' | 'c'
type IsSubTest = SubTest extends Test ? true : false // evaluates to true
如您所见SubTest
没有a
成员,但它可以分配给Test
,因此我们可以将foo
函数与这样的类型一起使用。
const arg: SubTest = 'b'
foo(arg)
一切都很好,没有错误。但这意味着您的条件arg === 'a'
永远不会得到满足,因为SubTest
中没有成员a
,这就是为什么 TS 不能假设在我们使用的条件内部a
,因为在这种情况下,该分支是完全无法访问的,内部将是never
型。我们甚至可以编写这样的函数并检查:
function foo(arg: SubTest) {
if (arg === 'a') { // compilation error, condition will be always false
console.log(arg);
}
}
好的,但即使为什么 TS 没有缩小到a
,因为条件很清楚,任何通过它的东西都会a
,毫无疑问!
让我们尝试手动重现原始条件下存在的相同类型保护,条件检查左侧值是否表示 x(从 Test 延伸(,等于右侧值表示 y(扩展 Test(
function isTestMember<X extends Test, Y extends Test>(x: X, y: Y): x is Y {
return x == y // error we cannot compare member of X with member of Y
}
如您所见,这种类型保护甚至无法编写。由于 X 和 Y 不能重叠,因此条件x === y
对X
和Y
的所有成员都无效
总之,TS不能考虑不能重叠的类型作为类型保护的条件,因此类型不缩小。