Description
TypeScript Version:
nightly (1.9.0-dev.20160619-1.0)
Code
Three functions that narrow three types to see how narrowing behaves. foo1
and foo2
are identical apart from which type guards they use.
// A discriminated union with three cases
interface A { type: 'A' }
interface B { type: 'B' }
interface C { type: 'C' }
// Custom type guards for A/B/C
function isA(x: A|B|C): x is A { return x.type === 'A'; }
function isB(x: A|B|C): x is B { return x.type === 'B'; }
function isC(x: A|B|C): x is C { return x.type === 'C'; }
// Using if/else with type guard functions
function foo1(x: A|B|C): any {
x // x is A | B | C
if (isA(x)) {
return x; // x is A
}
x // x is B | C
if (isB(x)) {
return x; // x is B
}
x // x is C
if (isC(x)) {
return x; // x is C
}
x // x is C, shouldn't x be 'never' here? <===== (1)
// If x was 'never', #8548 should apply here:
if (isA(x)) {
return x; // x is C & A <===== (2)
}
else {
x // x is C <===== (3)
}
}
// Using if/else with discriminated unions
function foo2(x: A|B|C): any {
x // x is A | B | C
if (x.type === 'A') {
return x; // x is A
}
x // x is B | C
if (x.type === 'B') {
return x; // x is B
}
x // x is C
if (x.type === 'C') {
return x; // x is C
}
x // x is A | B, shouldn't x be 'never' here? <===== (4)
// If x was 'never', #8548 should apply here:
if (x.type === 'A') {
return x; // x is A <===== (5)
}
else {
x // x is B <===== (6)
}
}
// Using switch/case with discriminated unions
function foo3(x: A|B|C): any {
x // x is A | B | C
switch (x.type) {
case 'A': return x; // x is A
case 'B': return x; // x is B
case 'C': return x; // x is C
default:
x // x is never <===== (7)
if (isA(x)) {
x // x is never, why not A due to #8548? <===== (8)
}
}
}
Expected behavior:
- The inferred types at (1)/(2)/(3) should be the same as the inferred types at (4)/(5)/(6)
- The inferred type at (1) and at (4) should be
never
, like it is at (7) - Type of x should be A at (8) due to Type guards as assertions #8548
Actual behavior:
See the comments in the code.
I wrote this code trying to understand what was going on in #9246 and #9254, and now I think I'm more confused. Firstly, foo1
and foo2
have identical logic, but tsc infers types differently in each. Secondly, I couldn't work out how to invoke the 'type guards as assertions' (#8548) behaviour. I thought it should kick in after (1) and (4), where the types have been exhausted and then another type guard appears. But control flow analysis never infers never
in either function. We do get never
inferred at (7), but then #8548 doesn't seem to apply at (8) where I expected it should.