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.
arg: stringthen it does narrow correctly, so it's definitely to do with T being a type variable. But if you don't have anextendsclause then it thinksTandstringhave no overlap, so there is no way to test whether it's caused byextendsspecifically. It seems like a bug, but maybe there is a reason for it.function foo(arg: Test) ...