Reputation: 35
type Test = 'a' | 'b' | 'c';
function foo<T extends Test>(arg: T) {
if (arg === 'a') {
console.log(arg);
}
}
like this.. I expect arg inferred 'a' in if block.
but ts infer as just T.
why this situation?
I think that extends
keyword has something...
Upvotes: 1
Views: 656
Reputation: 20162
The thing is that your type T
is not Test
but is a subset, we can say every type which can be used instead of T
, and every for union means a type which has the same or less union members. Consider example type which extends Test
:
type Test = 'a' | 'b' | 'c';
type SubTest = 'b' | 'c'
type IsSubTest = SubTest extends Test ? true : false // evaluates to true
As you can see SubTest
doesn't have a
member but it is assignable to Test
, therefore we can use foo
function with such a type.
const arg: SubTest = 'b'
foo(arg)
Everything good, no error. But that means that your condition arg === 'a'
will never be met, as there is no member a
in SubTest
, that is why TS cannot assume that inside the condition we work with a
, as in this situation this branch is totally not reachable and inside it will be type never
. We can even write such function and check:
function foo(arg: SubTest) {
if (arg === 'a') { // compilation error, condition will be always false
console.log(arg);
}
}
Ok but even though why TS is not narrowing to a
, as the condition is clear anything which will pass it will be a
, no doubt here!
Lets try reproduce manually the same type guard which exists in the original condition, and the condition checks if the left value say x (which extends from Test), equals to the right value say y(which extends 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
}
As you can see such typeguard cannot be even written. As X and Y can not overlap, therefor the condition x === y
is not valid for all members of X
and Y
In summary TS cannot consider conditions on types which can not overlap as type guards, therefor type is not narrowed.
Upvotes: 1