Skip to content

Commit 1d8fb49

Browse files
committed
Source type parameter extending union works even when target is a type parameter
1 parent 65790d1 commit 1d8fb49

19 files changed

+540
-22
lines changed

src/compiler/checker.ts

+22-22
Original file line numberDiff line numberDiff line change
@@ -4016,6 +4016,7 @@ module ts {
40164016
if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True;
40174017
}
40184018
}
4019+
let saveErrorInfo = errorInfo;
40194020
if (source.flags & TypeFlags.Union || target.flags & TypeFlags.Union) {
40204021
if (relation === identityRelation) {
40214022
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union) {
@@ -4054,32 +4055,31 @@ module ts {
40544055
return result;
40554056
}
40564057
}
4057-
else {
4058-
let saveErrorInfo = errorInfo;
4059-
if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
4060-
// We have type references to same target type, see if relationship holds for all type arguments
4061-
if (result = typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, reportErrors)) {
4062-
return result;
4063-
}
4058+
else if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
4059+
// We have type references to same target type, see if relationship holds for all type arguments
4060+
if (result = typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, reportErrors)) {
4061+
return result;
40644062
}
4065-
// Even if relationship doesn't hold for type arguments, it may hold in a structural comparison
4066-
// Report structural errors only if we haven't reported any errors yet
4067-
let reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
4068-
// identity relation does not use apparent type
4069-
let sourceOrApparentType = relation === identityRelation ? source : getApparentType(source);
4070-
if (sourceOrApparentType.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) {
4071-
if (result = objectTypeRelatedTo(sourceOrApparentType, <ObjectType>target, reportStructuralErrors)) {
4072-
errorInfo = saveErrorInfo;
4073-
return result;
4074-
}
4063+
}
4064+
4065+
// Even if relationship doesn't hold for type arguments, it may hold in a structural comparison
4066+
// Report structural errors only if we haven't reported any errors yet
4067+
let reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
4068+
// identity relation does not use apparent type
4069+
let sourceOrApparentType = relation === identityRelation ? source : getApparentType(source);
4070+
if (sourceOrApparentType.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) {
4071+
if (result = objectTypeRelatedTo(sourceOrApparentType, <ObjectType>target, reportStructuralErrors)) {
4072+
errorInfo = saveErrorInfo;
4073+
return result;
40754074
}
4076-
else if (source.flags & TypeFlags.TypeParameter && sourceOrApparentType.flags & TypeFlags.Union) {
4077-
if (result = isRelatedTo(sourceOrApparentType, <ObjectType>target, reportStructuralErrors)) {
4078-
errorInfo = saveErrorInfo;
4079-
return result;
4080-
}
4075+
}
4076+
else if (source.flags & TypeFlags.TypeParameter && sourceOrApparentType.flags & TypeFlags.Union) {
4077+
if (result = isRelatedTo(sourceOrApparentType, <ObjectType>target, reportStructuralErrors)) {
4078+
errorInfo = saveErrorInfo;
4079+
return result;
40814080
}
40824081
}
4082+
40834083
if (reportErrors) {
40844084
headMessage = headMessage || Diagnostics.Type_0_is_not_assignable_to_type_1;
40854085
let sourceType = typeToString(source);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//// [typeParameterDiamond1.ts]
2+
function diamondTop<Top>() {
3+
function diamondMiddle<T extends Top, U extends Top>() {
4+
function diamondBottom<Bottom extends T | U>() {
5+
var top: Top;
6+
var middle: T | U;
7+
var bottom: Bottom;
8+
9+
top = middle;
10+
middle = bottom;
11+
top = bottom;
12+
}
13+
}
14+
}
15+
16+
//// [typeParameterDiamond1.js]
17+
function diamondTop() {
18+
function diamondMiddle() {
19+
function diamondBottom() {
20+
var top;
21+
var middle;
22+
var bottom;
23+
top = middle;
24+
middle = bottom;
25+
top = bottom;
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
=== tests/cases/compiler/typeParameterDiamond1.ts ===
2+
function diamondTop<Top>() {
3+
>diamondTop : <Top>() => void, Symbol(diamondTop, Decl(typeParameterDiamond1.ts, 0, 0))
4+
>Top : Top, Symbol(Top, Decl(typeParameterDiamond1.ts, 0, 20))
5+
6+
function diamondMiddle<T extends Top, U extends Top>() {
7+
>diamondMiddle : <T extends Top, U extends Top>() => void, Symbol(diamondMiddle, Decl(typeParameterDiamond1.ts, 0, 28))
8+
>T : T, Symbol(T, Decl(typeParameterDiamond1.ts, 1, 27))
9+
>Top : Top, Symbol(Top, Decl(typeParameterDiamond1.ts, 0, 20))
10+
>U : U, Symbol(U, Decl(typeParameterDiamond1.ts, 1, 41))
11+
>Top : Top, Symbol(Top, Decl(typeParameterDiamond1.ts, 0, 20))
12+
13+
function diamondBottom<Bottom extends T | U>() {
14+
>diamondBottom : <Bottom extends T | U>() => void, Symbol(diamondBottom, Decl(typeParameterDiamond1.ts, 1, 60))
15+
>Bottom : Bottom, Symbol(Bottom, Decl(typeParameterDiamond1.ts, 2, 31))
16+
>T : T, Symbol(T, Decl(typeParameterDiamond1.ts, 1, 27))
17+
>U : U, Symbol(U, Decl(typeParameterDiamond1.ts, 1, 41))
18+
19+
var top: Top;
20+
>top : Top, Symbol(top, Decl(typeParameterDiamond1.ts, 3, 15))
21+
>Top : Top, Symbol(Top, Decl(typeParameterDiamond1.ts, 0, 20))
22+
23+
var middle: T | U;
24+
>middle : T | U, Symbol(middle, Decl(typeParameterDiamond1.ts, 4, 15))
25+
>T : T, Symbol(T, Decl(typeParameterDiamond1.ts, 1, 27))
26+
>U : U, Symbol(U, Decl(typeParameterDiamond1.ts, 1, 41))
27+
28+
var bottom: Bottom;
29+
>bottom : Bottom, Symbol(bottom, Decl(typeParameterDiamond1.ts, 5, 15))
30+
>Bottom : Bottom, Symbol(Bottom, Decl(typeParameterDiamond1.ts, 2, 31))
31+
32+
top = middle;
33+
>top = middle : T | U
34+
>top : Top, Symbol(top, Decl(typeParameterDiamond1.ts, 3, 15))
35+
>middle : T | U, Symbol(middle, Decl(typeParameterDiamond1.ts, 4, 15))
36+
37+
middle = bottom;
38+
>middle = bottom : Bottom
39+
>middle : T | U, Symbol(middle, Decl(typeParameterDiamond1.ts, 4, 15))
40+
>bottom : Bottom, Symbol(bottom, Decl(typeParameterDiamond1.ts, 5, 15))
41+
42+
top = bottom;
43+
>top = bottom : Bottom
44+
>top : Top, Symbol(top, Decl(typeParameterDiamond1.ts, 3, 15))
45+
>bottom : Bottom, Symbol(bottom, Decl(typeParameterDiamond1.ts, 5, 15))
46+
}
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
tests/cases/compiler/typeParameterDiamond2.ts(8,13): error TS2322: Type 'T | U' is not assignable to type 'Top'.
2+
Type 'U' is not assignable to type 'Top'.
3+
tests/cases/compiler/typeParameterDiamond2.ts(10,13): error TS2322: Type 'Bottom' is not assignable to type 'Top'.
4+
Type 'T | U' is not assignable to type 'Top'.
5+
Type 'U' is not assignable to type 'Top'.
6+
7+
8+
==== tests/cases/compiler/typeParameterDiamond2.ts (2 errors) ====
9+
function diamondTop<Top>() {
10+
function diamondMiddle<T extends Top, U>() {
11+
function diamondBottom<Bottom extends T | U>() {
12+
var top: Top;
13+
var middle: T | U;
14+
var bottom: Bottom;
15+
16+
top = middle;
17+
~~~
18+
!!! error TS2322: Type 'T | U' is not assignable to type 'Top'.
19+
!!! error TS2322: Type 'U' is not assignable to type 'Top'.
20+
middle = bottom;
21+
top = bottom;
22+
~~~
23+
!!! error TS2322: Type 'Bottom' is not assignable to type 'Top'.
24+
!!! error TS2322: Type 'T | U' is not assignable to type 'Top'.
25+
!!! error TS2322: Type 'U' is not assignable to type 'Top'.
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//// [typeParameterDiamond2.ts]
2+
function diamondTop<Top>() {
3+
function diamondMiddle<T extends Top, U>() {
4+
function diamondBottom<Bottom extends T | U>() {
5+
var top: Top;
6+
var middle: T | U;
7+
var bottom: Bottom;
8+
9+
top = middle;
10+
middle = bottom;
11+
top = bottom;
12+
}
13+
}
14+
}
15+
16+
//// [typeParameterDiamond2.js]
17+
function diamondTop() {
18+
function diamondMiddle() {
19+
function diamondBottom() {
20+
var top;
21+
var middle;
22+
var bottom;
23+
top = middle;
24+
middle = bottom;
25+
top = bottom;
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
tests/cases/compiler/typeParameterDiamond3.ts(8,13): error TS2322: Type 'T | U' is not assignable to type 'Top'.
2+
Type 'T' is not assignable to type 'Top'.
3+
tests/cases/compiler/typeParameterDiamond3.ts(9,13): error TS2322: Type 'Bottom' is not assignable to type 'T | U'.
4+
Type 'Bottom' is not assignable to type 'U'.
5+
Type 'Top | T | U' is not assignable to type 'U'.
6+
Type 'Top' is not assignable to type 'U'.
7+
tests/cases/compiler/typeParameterDiamond3.ts(10,13): error TS2322: Type 'Bottom' is not assignable to type 'Top'.
8+
Type 'Top | T | U' is not assignable to type 'Top'.
9+
Type 'T' is not assignable to type 'Top'.
10+
11+
12+
==== tests/cases/compiler/typeParameterDiamond3.ts (3 errors) ====
13+
function diamondTop<Top>() {
14+
function diamondMiddle<T, U>() {
15+
function diamondBottom<Bottom extends Top | T | U>() {
16+
var top: Top;
17+
var middle: T | U;
18+
var bottom: Bottom;
19+
20+
top = middle;
21+
~~~
22+
!!! error TS2322: Type 'T | U' is not assignable to type 'Top'.
23+
!!! error TS2322: Type 'T' is not assignable to type 'Top'.
24+
middle = bottom;
25+
~~~~~~
26+
!!! error TS2322: Type 'Bottom' is not assignable to type 'T | U'.
27+
!!! error TS2322: Type 'Bottom' is not assignable to type 'U'.
28+
!!! error TS2322: Type 'Top | T | U' is not assignable to type 'U'.
29+
!!! error TS2322: Type 'Top' is not assignable to type 'U'.
30+
top = bottom;
31+
~~~
32+
!!! error TS2322: Type 'Bottom' is not assignable to type 'Top'.
33+
!!! error TS2322: Type 'Top | T | U' is not assignable to type 'Top'.
34+
!!! error TS2322: Type 'T' is not assignable to type 'Top'.
35+
}
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//// [typeParameterDiamond3.ts]
2+
function diamondTop<Top>() {
3+
function diamondMiddle<T, U>() {
4+
function diamondBottom<Bottom extends Top | T | U>() {
5+
var top: Top;
6+
var middle: T | U;
7+
var bottom: Bottom;
8+
9+
top = middle;
10+
middle = bottom;
11+
top = bottom;
12+
}
13+
}
14+
}
15+
16+
//// [typeParameterDiamond3.js]
17+
function diamondTop() {
18+
function diamondMiddle() {
19+
function diamondBottom() {
20+
var top;
21+
var middle;
22+
var bottom;
23+
top = middle;
24+
middle = bottom;
25+
top = bottom;
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
tests/cases/compiler/typeParameterDiamond4.ts(8,13): error TS2322: Type 'Top | T | U' is not assignable to type 'Top'.
2+
Type 'T' is not assignable to type 'Top'.
3+
tests/cases/compiler/typeParameterDiamond4.ts(10,13): error TS2322: Type 'Bottom' is not assignable to type 'Top'.
4+
Type 'Top | T | U' is not assignable to type 'Top'.
5+
Type 'T' is not assignable to type 'Top'.
6+
7+
8+
==== tests/cases/compiler/typeParameterDiamond4.ts (2 errors) ====
9+
function diamondTop<Top>() {
10+
function diamondMiddle<T, U>() {
11+
function diamondBottom<Bottom extends Top | T | U>() {
12+
var top: Top;
13+
var middle: Top | T | U;
14+
var bottom: Bottom;
15+
16+
top = middle;
17+
~~~
18+
!!! error TS2322: Type 'Top | T | U' is not assignable to type 'Top'.
19+
!!! error TS2322: Type 'T' is not assignable to type 'Top'.
20+
middle = bottom;
21+
top = bottom;
22+
~~~
23+
!!! error TS2322: Type 'Bottom' is not assignable to type 'Top'.
24+
!!! error TS2322: Type 'Top | T | U' is not assignable to type 'Top'.
25+
!!! error TS2322: Type 'T' is not assignable to type 'Top'.
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//// [typeParameterDiamond4.ts]
2+
function diamondTop<Top>() {
3+
function diamondMiddle<T, U>() {
4+
function diamondBottom<Bottom extends Top | T | U>() {
5+
var top: Top;
6+
var middle: Top | T | U;
7+
var bottom: Bottom;
8+
9+
top = middle;
10+
middle = bottom;
11+
top = bottom;
12+
}
13+
}
14+
}
15+
16+
//// [typeParameterDiamond4.js]
17+
function diamondTop() {
18+
function diamondMiddle() {
19+
function diamondBottom() {
20+
var top;
21+
var middle;
22+
var bottom;
23+
top = middle;
24+
middle = bottom;
25+
top = bottom;
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//// [typeParameterExtendingUnion1.ts]
2+
class Animal { run() { } }
3+
class Cat extends Animal { meow }
4+
class Dog extends Animal { woof }
5+
6+
function run(a: Animal) {
7+
a.run();
8+
}
9+
10+
function f<T extends Cat | Dog>(a: T) {
11+
a.run();
12+
run(a);
13+
}
14+
15+
//// [typeParameterExtendingUnion1.js]
16+
var __extends = this.__extends || function (d, b) {
17+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
18+
function __() { this.constructor = d; }
19+
__.prototype = b.prototype;
20+
d.prototype = new __();
21+
};
22+
var Animal = (function () {
23+
function Animal() {
24+
}
25+
Animal.prototype.run = function () { };
26+
return Animal;
27+
})();
28+
var Cat = (function (_super) {
29+
__extends(Cat, _super);
30+
function Cat() {
31+
_super.apply(this, arguments);
32+
}
33+
return Cat;
34+
})(Animal);
35+
var Dog = (function (_super) {
36+
__extends(Dog, _super);
37+
function Dog() {
38+
_super.apply(this, arguments);
39+
}
40+
return Dog;
41+
})(Animal);
42+
function run(a) {
43+
a.run();
44+
}
45+
function f(a) {
46+
a.run();
47+
run(a);
48+
}

0 commit comments

Comments
 (0)