TypeScript Version: 2.2.0-dev.20161114
Code
// --target es5
interface A { x: 0; y: C[]; }
interface B { x: 1; y: CD[]; }
interface C { x: 2; }
interface D { x: 3; }
type AB = A | B;
type CD = C | D;
declare let x: AB, y: CD;
for (y of x.y);
Expected behavior:
This should not error, as the element type of x.y should be CD, since the type of x.y is C[] | CD[].
Actual behavior:
Reports the error:
error TS2322: Type 'string | C | D' is not assignable to type 'CD'.
Type 'string' is not assignable to type 'CD'.
The reason for this behavior is due to two causes:
- We do not perform subtype reduction for alias types, as they could be recursive and subtype reduction can be expensive. As a result, we do not reduce
x.y from C[] | D[] to CD[] prior to calling checkElementTypeOfArrayOrString
- In
checkElementTypeOfArrayOrString we call getUnionType passing in a filtered set of types (which still contains the types C[] and CD[]), passing true to the subtypeReduction parameter. This results in us reducing C[] | CD[] to CD[]. Unfortunately, this results in a false positive for the hasStringConstituent check later in the function.
As a result, we accidentally introduce stringType as a constituent, resulting in a type of string | C | D.
TypeScript Version: 2.2.0-dev.20161114
Code
Expected behavior:
This should not error, as the element type of
x.yshould beCD, since the type ofx.yisC[] | CD[].Actual behavior:
Reports the error:
The reason for this behavior is due to two causes:
x.yfromC[] | D[]toCD[]prior to callingcheckElementTypeOfArrayOrStringcheckElementTypeOfArrayOrStringwe callgetUnionTypepassing in a filtered set of types (which still contains the typesC[]andCD[]), passingtrueto thesubtypeReductionparameter. This results in us reducingC[] | CD[]toCD[]. Unfortunately, this results in a false positive for thehasStringConstituentcheck later in the function.As a result, we accidentally introduce
stringTypeas a constituent, resulting in a type ofstring | C | D.