Merge pull request #2778 from Microsoft/typeParameterUnionAssignability

Change assignability to account for type parameters extending unions
This commit is contained in:
Jason Freeman 2015-04-15 15:52:57 -07:00
commit b1acce0e5f
19 changed files with 542 additions and 14 deletions

View file

@ -4016,6 +4016,7 @@ module ts {
if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True;
}
}
let saveErrorInfo = errorInfo;
if (source.flags & TypeFlags.Union || target.flags & TypeFlags.Union) {
if (relation === identityRelation) {
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union) {
@ -4054,25 +4055,34 @@ module ts {
return result;
}
}
else {
let saveErrorInfo = errorInfo;
if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
// We have type references to same target type, see if relationship holds for all type arguments
if (result = typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, reportErrors)) {
return result;
}
else if (source.flags & TypeFlags.Reference && target.flags & TypeFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
// We have type references to same target type, see if relationship holds for all type arguments
if (result = typesRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, reportErrors)) {
return result;
}
// Even if relationship doesn't hold for type arguments, it may hold in a structural comparison
// Report structural errors only if we haven't reported any errors yet
let reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
// identity relation does not use apparent type
let sourceOrApparentType = relation === identityRelation ? source : getApparentType(source);
if (sourceOrApparentType.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType &&
(result = objectTypeRelatedTo(sourceOrApparentType, <ObjectType>target, reportStructuralErrors))) {
}
// Even if relationship doesn't hold for unions, type parameters, or generic type references,
// it may hold in a structural comparison.
// Report structural errors only if we haven't reported any errors yet
let reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo;
// identity relation does not use apparent type
let sourceOrApparentType = relation === identityRelation ? source : getApparentType(source);
if (sourceOrApparentType.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) {
if (result = objectTypeRelatedTo(sourceOrApparentType, <ObjectType>target, reportStructuralErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}
else if (source.flags & TypeFlags.TypeParameter && sourceOrApparentType.flags & TypeFlags.Union) {
// We clear the errors first because the following check often gives a better error than
// the union comparison above if it is applicable.
errorInfo = saveErrorInfo;
if (result = isRelatedTo(sourceOrApparentType, target, reportErrors)) {
return result;
}
}
if (reportErrors) {
headMessage = headMessage || Diagnostics.Type_0_is_not_assignable_to_type_1;
let sourceType = typeToString(source);

View file

@ -0,0 +1,28 @@
//// [typeParameterDiamond1.ts]
function diamondTop<Top>() {
function diamondMiddle<T extends Top, U extends Top>() {
function diamondBottom<Bottom extends T | U>() {
var top: Top;
var middle: T | U;
var bottom: Bottom;
top = middle;
middle = bottom;
top = bottom;
}
}
}
//// [typeParameterDiamond1.js]
function diamondTop() {
function diamondMiddle() {
function diamondBottom() {
var top;
var middle;
var bottom;
top = middle;
middle = bottom;
top = bottom;
}
}
}

View file

@ -0,0 +1,48 @@
=== tests/cases/compiler/typeParameterDiamond1.ts ===
function diamondTop<Top>() {
>diamondTop : <Top>() => void, Symbol(diamondTop, Decl(typeParameterDiamond1.ts, 0, 0))
>Top : Top, Symbol(Top, Decl(typeParameterDiamond1.ts, 0, 20))
function diamondMiddle<T extends Top, U extends Top>() {
>diamondMiddle : <T extends Top, U extends Top>() => void, Symbol(diamondMiddle, Decl(typeParameterDiamond1.ts, 0, 28))
>T : T, Symbol(T, Decl(typeParameterDiamond1.ts, 1, 27))
>Top : Top, Symbol(Top, Decl(typeParameterDiamond1.ts, 0, 20))
>U : U, Symbol(U, Decl(typeParameterDiamond1.ts, 1, 41))
>Top : Top, Symbol(Top, Decl(typeParameterDiamond1.ts, 0, 20))
function diamondBottom<Bottom extends T | U>() {
>diamondBottom : <Bottom extends T | U>() => void, Symbol(diamondBottom, Decl(typeParameterDiamond1.ts, 1, 60))
>Bottom : Bottom, Symbol(Bottom, Decl(typeParameterDiamond1.ts, 2, 31))
>T : T, Symbol(T, Decl(typeParameterDiamond1.ts, 1, 27))
>U : U, Symbol(U, Decl(typeParameterDiamond1.ts, 1, 41))
var top: Top;
>top : Top, Symbol(top, Decl(typeParameterDiamond1.ts, 3, 15))
>Top : Top, Symbol(Top, Decl(typeParameterDiamond1.ts, 0, 20))
var middle: T | U;
>middle : T | U, Symbol(middle, Decl(typeParameterDiamond1.ts, 4, 15))
>T : T, Symbol(T, Decl(typeParameterDiamond1.ts, 1, 27))
>U : U, Symbol(U, Decl(typeParameterDiamond1.ts, 1, 41))
var bottom: Bottom;
>bottom : Bottom, Symbol(bottom, Decl(typeParameterDiamond1.ts, 5, 15))
>Bottom : Bottom, Symbol(Bottom, Decl(typeParameterDiamond1.ts, 2, 31))
top = middle;
>top = middle : T | U
>top : Top, Symbol(top, Decl(typeParameterDiamond1.ts, 3, 15))
>middle : T | U, Symbol(middle, Decl(typeParameterDiamond1.ts, 4, 15))
middle = bottom;
>middle = bottom : Bottom
>middle : T | U, Symbol(middle, Decl(typeParameterDiamond1.ts, 4, 15))
>bottom : Bottom, Symbol(bottom, Decl(typeParameterDiamond1.ts, 5, 15))
top = bottom;
>top = bottom : Bottom
>top : Top, Symbol(top, Decl(typeParameterDiamond1.ts, 3, 15))
>bottom : Bottom, Symbol(bottom, Decl(typeParameterDiamond1.ts, 5, 15))
}
}
}

View file

@ -0,0 +1,28 @@
tests/cases/compiler/typeParameterDiamond2.ts(8,13): error TS2322: Type 'T | U' is not assignable to type 'Top'.
Type 'U' is not assignable to type 'Top'.
tests/cases/compiler/typeParameterDiamond2.ts(10,13): error TS2322: Type 'Bottom' is not assignable to type 'Top'.
Type 'T | U' is not assignable to type 'Top'.
Type 'U' is not assignable to type 'Top'.
==== tests/cases/compiler/typeParameterDiamond2.ts (2 errors) ====
function diamondTop<Top>() {
function diamondMiddle<T extends Top, U>() {
function diamondBottom<Bottom extends T | U>() {
var top: Top;
var middle: T | U;
var bottom: Bottom;
top = middle;
~~~
!!! error TS2322: Type 'T | U' is not assignable to type 'Top'.
!!! error TS2322: Type 'U' is not assignable to type 'Top'.
middle = bottom;
top = bottom;
~~~
!!! error TS2322: Type 'Bottom' is not assignable to type 'Top'.
!!! error TS2322: Type 'T | U' is not assignable to type 'Top'.
!!! error TS2322: Type 'U' is not assignable to type 'Top'.
}
}
}

View file

@ -0,0 +1,28 @@
//// [typeParameterDiamond2.ts]
function diamondTop<Top>() {
function diamondMiddle<T extends Top, U>() {
function diamondBottom<Bottom extends T | U>() {
var top: Top;
var middle: T | U;
var bottom: Bottom;
top = middle;
middle = bottom;
top = bottom;
}
}
}
//// [typeParameterDiamond2.js]
function diamondTop() {
function diamondMiddle() {
function diamondBottom() {
var top;
var middle;
var bottom;
top = middle;
middle = bottom;
top = bottom;
}
}
}

View file

@ -0,0 +1,37 @@
tests/cases/compiler/typeParameterDiamond3.ts(8,13): error TS2322: Type 'T | U' is not assignable to type 'Top'.
Type 'T' is not assignable to type 'Top'.
tests/cases/compiler/typeParameterDiamond3.ts(9,13): error TS2322: Type 'Bottom' is not assignable to type 'T | U'.
Type 'Top | T | U' is not assignable to type 'T | U'.
Type 'Top' is not assignable to type 'T | U'.
Type 'Top' is not assignable to type 'U'.
tests/cases/compiler/typeParameterDiamond3.ts(10,13): error TS2322: Type 'Bottom' is not assignable to type 'Top'.
Type 'Top | T | U' is not assignable to type 'Top'.
Type 'T' is not assignable to type 'Top'.
==== tests/cases/compiler/typeParameterDiamond3.ts (3 errors) ====
function diamondTop<Top>() {
function diamondMiddle<T, U>() {
function diamondBottom<Bottom extends Top | T | U>() {
var top: Top;
var middle: T | U;
var bottom: Bottom;
top = middle;
~~~
!!! error TS2322: Type 'T | U' is not assignable to type 'Top'.
!!! error TS2322: Type 'T' is not assignable to type 'Top'.
middle = bottom;
~~~~~~
!!! error TS2322: Type 'Bottom' is not assignable to type 'T | U'.
!!! error TS2322: Type 'Top | T | U' is not assignable to type 'T | U'.
!!! error TS2322: Type 'Top' is not assignable to type 'T | U'.
!!! error TS2322: Type 'Top' is not assignable to type 'U'.
top = bottom;
~~~
!!! error TS2322: Type 'Bottom' is not assignable to type 'Top'.
!!! error TS2322: Type 'Top | T | U' is not assignable to type 'Top'.
!!! error TS2322: Type 'T' is not assignable to type 'Top'.
}
}
}

View file

@ -0,0 +1,28 @@
//// [typeParameterDiamond3.ts]
function diamondTop<Top>() {
function diamondMiddle<T, U>() {
function diamondBottom<Bottom extends Top | T | U>() {
var top: Top;
var middle: T | U;
var bottom: Bottom;
top = middle;
middle = bottom;
top = bottom;
}
}
}
//// [typeParameterDiamond3.js]
function diamondTop() {
function diamondMiddle() {
function diamondBottom() {
var top;
var middle;
var bottom;
top = middle;
middle = bottom;
top = bottom;
}
}
}

View file

@ -0,0 +1,28 @@
tests/cases/compiler/typeParameterDiamond4.ts(8,13): error TS2322: Type 'Top | T | U' is not assignable to type 'Top'.
Type 'T' is not assignable to type 'Top'.
tests/cases/compiler/typeParameterDiamond4.ts(10,13): error TS2322: Type 'Bottom' is not assignable to type 'Top'.
Type 'Top | T | U' is not assignable to type 'Top'.
Type 'T' is not assignable to type 'Top'.
==== tests/cases/compiler/typeParameterDiamond4.ts (2 errors) ====
function diamondTop<Top>() {
function diamondMiddle<T, U>() {
function diamondBottom<Bottom extends Top | T | U>() {
var top: Top;
var middle: Top | T | U;
var bottom: Bottom;
top = middle;
~~~
!!! error TS2322: Type 'Top | T | U' is not assignable to type 'Top'.
!!! error TS2322: Type 'T' is not assignable to type 'Top'.
middle = bottom;
top = bottom;
~~~
!!! error TS2322: Type 'Bottom' is not assignable to type 'Top'.
!!! error TS2322: Type 'Top | T | U' is not assignable to type 'Top'.
!!! error TS2322: Type 'T' is not assignable to type 'Top'.
}
}
}

View file

@ -0,0 +1,28 @@
//// [typeParameterDiamond4.ts]
function diamondTop<Top>() {
function diamondMiddle<T, U>() {
function diamondBottom<Bottom extends Top | T | U>() {
var top: Top;
var middle: Top | T | U;
var bottom: Bottom;
top = middle;
middle = bottom;
top = bottom;
}
}
}
//// [typeParameterDiamond4.js]
function diamondTop() {
function diamondMiddle() {
function diamondBottom() {
var top;
var middle;
var bottom;
top = middle;
middle = bottom;
top = bottom;
}
}
}

View file

@ -0,0 +1,48 @@
//// [typeParameterExtendingUnion1.ts]
class Animal { run() { } }
class Cat extends Animal { meow }
class Dog extends Animal { woof }
function run(a: Animal) {
a.run();
}
function f<T extends Cat | Dog>(a: T) {
a.run();
run(a);
}
//// [typeParameterExtendingUnion1.js]
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
var Animal = (function () {
function Animal() {
}
Animal.prototype.run = function () { };
return Animal;
})();
var Cat = (function (_super) {
__extends(Cat, _super);
function Cat() {
_super.apply(this, arguments);
}
return Cat;
})(Animal);
var Dog = (function (_super) {
__extends(Dog, _super);
function Dog() {
_super.apply(this, arguments);
}
return Dog;
})(Animal);
function run(a) {
a.run();
}
function f(a) {
a.run();
run(a);
}

View file

@ -0,0 +1,46 @@
=== tests/cases/compiler/typeParameterExtendingUnion1.ts ===
class Animal { run() { } }
>Animal : Animal, Symbol(Animal, Decl(typeParameterExtendingUnion1.ts, 0, 0))
>run : () => void, Symbol(run, Decl(typeParameterExtendingUnion1.ts, 0, 14))
class Cat extends Animal { meow }
>Cat : Cat, Symbol(Cat, Decl(typeParameterExtendingUnion1.ts, 0, 26))
>Animal : Animal, Symbol(Animal, Decl(typeParameterExtendingUnion1.ts, 0, 0))
>meow : any, Symbol(meow, Decl(typeParameterExtendingUnion1.ts, 1, 26))
class Dog extends Animal { woof }
>Dog : Dog, Symbol(Dog, Decl(typeParameterExtendingUnion1.ts, 1, 33))
>Animal : Animal, Symbol(Animal, Decl(typeParameterExtendingUnion1.ts, 0, 0))
>woof : any, Symbol(woof, Decl(typeParameterExtendingUnion1.ts, 2, 26))
function run(a: Animal) {
>run : (a: Animal) => void, Symbol(run, Decl(typeParameterExtendingUnion1.ts, 2, 33))
>a : Animal, Symbol(a, Decl(typeParameterExtendingUnion1.ts, 4, 13))
>Animal : Animal, Symbol(Animal, Decl(typeParameterExtendingUnion1.ts, 0, 0))
a.run();
>a.run() : void
>a.run : () => void, Symbol(Animal.run, Decl(typeParameterExtendingUnion1.ts, 0, 14))
>a : Animal, Symbol(a, Decl(typeParameterExtendingUnion1.ts, 4, 13))
>run : () => void, Symbol(Animal.run, Decl(typeParameterExtendingUnion1.ts, 0, 14))
}
function f<T extends Cat | Dog>(a: T) {
>f : <T extends Cat | Dog>(a: T) => void, Symbol(f, Decl(typeParameterExtendingUnion1.ts, 6, 1))
>T : T, Symbol(T, Decl(typeParameterExtendingUnion1.ts, 8, 11))
>Cat : Cat, Symbol(Cat, Decl(typeParameterExtendingUnion1.ts, 0, 26))
>Dog : Dog, Symbol(Dog, Decl(typeParameterExtendingUnion1.ts, 1, 33))
>a : T, Symbol(a, Decl(typeParameterExtendingUnion1.ts, 8, 32))
>T : T, Symbol(T, Decl(typeParameterExtendingUnion1.ts, 8, 11))
a.run();
>a.run() : void
>a.run : () => void, Symbol(run, Decl(typeParameterExtendingUnion1.ts, 0, 14), Decl(typeParameterExtendingUnion1.ts, 0, 14))
>a : T, Symbol(a, Decl(typeParameterExtendingUnion1.ts, 8, 32))
>run : () => void, Symbol(run, Decl(typeParameterExtendingUnion1.ts, 0, 14), Decl(typeParameterExtendingUnion1.ts, 0, 14))
run(a);
>run(a) : void
>run : (a: Animal) => void, Symbol(run, Decl(typeParameterExtendingUnion1.ts, 2, 33))
>a : T, Symbol(a, Decl(typeParameterExtendingUnion1.ts, 8, 32))
}

View file

@ -0,0 +1,48 @@
//// [typeParameterExtendingUnion2.ts]
class Animal { run() { } }
class Cat extends Animal { meow }
class Dog extends Animal { woof }
function run(a: Cat | Dog) {
a.run();
}
function f<T extends Cat | Dog>(a: T) {
a.run();
run(a);
}
//// [typeParameterExtendingUnion2.js]
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
var Animal = (function () {
function Animal() {
}
Animal.prototype.run = function () { };
return Animal;
})();
var Cat = (function (_super) {
__extends(Cat, _super);
function Cat() {
_super.apply(this, arguments);
}
return Cat;
})(Animal);
var Dog = (function (_super) {
__extends(Dog, _super);
function Dog() {
_super.apply(this, arguments);
}
return Dog;
})(Animal);
function run(a) {
a.run();
}
function f(a) {
a.run();
run(a);
}

View file

@ -0,0 +1,47 @@
=== tests/cases/compiler/typeParameterExtendingUnion2.ts ===
class Animal { run() { } }
>Animal : Animal, Symbol(Animal, Decl(typeParameterExtendingUnion2.ts, 0, 0))
>run : () => void, Symbol(run, Decl(typeParameterExtendingUnion2.ts, 0, 14))
class Cat extends Animal { meow }
>Cat : Cat, Symbol(Cat, Decl(typeParameterExtendingUnion2.ts, 0, 26))
>Animal : Animal, Symbol(Animal, Decl(typeParameterExtendingUnion2.ts, 0, 0))
>meow : any, Symbol(meow, Decl(typeParameterExtendingUnion2.ts, 1, 26))
class Dog extends Animal { woof }
>Dog : Dog, Symbol(Dog, Decl(typeParameterExtendingUnion2.ts, 1, 33))
>Animal : Animal, Symbol(Animal, Decl(typeParameterExtendingUnion2.ts, 0, 0))
>woof : any, Symbol(woof, Decl(typeParameterExtendingUnion2.ts, 2, 26))
function run(a: Cat | Dog) {
>run : (a: Cat | Dog) => void, Symbol(run, Decl(typeParameterExtendingUnion2.ts, 2, 33))
>a : Cat | Dog, Symbol(a, Decl(typeParameterExtendingUnion2.ts, 4, 13))
>Cat : Cat, Symbol(Cat, Decl(typeParameterExtendingUnion2.ts, 0, 26))
>Dog : Dog, Symbol(Dog, Decl(typeParameterExtendingUnion2.ts, 1, 33))
a.run();
>a.run() : void
>a.run : () => void, Symbol(run, Decl(typeParameterExtendingUnion2.ts, 0, 14), Decl(typeParameterExtendingUnion2.ts, 0, 14))
>a : Cat | Dog, Symbol(a, Decl(typeParameterExtendingUnion2.ts, 4, 13))
>run : () => void, Symbol(run, Decl(typeParameterExtendingUnion2.ts, 0, 14), Decl(typeParameterExtendingUnion2.ts, 0, 14))
}
function f<T extends Cat | Dog>(a: T) {
>f : <T extends Cat | Dog>(a: T) => void, Symbol(f, Decl(typeParameterExtendingUnion2.ts, 6, 1))
>T : T, Symbol(T, Decl(typeParameterExtendingUnion2.ts, 8, 11))
>Cat : Cat, Symbol(Cat, Decl(typeParameterExtendingUnion2.ts, 0, 26))
>Dog : Dog, Symbol(Dog, Decl(typeParameterExtendingUnion2.ts, 1, 33))
>a : T, Symbol(a, Decl(typeParameterExtendingUnion2.ts, 8, 32))
>T : T, Symbol(T, Decl(typeParameterExtendingUnion2.ts, 8, 11))
a.run();
>a.run() : void
>a.run : () => void, Symbol(run, Decl(typeParameterExtendingUnion2.ts, 0, 14), Decl(typeParameterExtendingUnion2.ts, 0, 14))
>a : T, Symbol(a, Decl(typeParameterExtendingUnion2.ts, 8, 32))
>run : () => void, Symbol(run, Decl(typeParameterExtendingUnion2.ts, 0, 14), Decl(typeParameterExtendingUnion2.ts, 0, 14))
run(a);
>run(a) : void
>run : (a: Cat | Dog) => void, Symbol(run, Decl(typeParameterExtendingUnion2.ts, 2, 33))
>a : T, Symbol(a, Decl(typeParameterExtendingUnion2.ts, 8, 32))
}

View file

@ -0,0 +1,13 @@
function diamondTop<Top>() {
function diamondMiddle<T extends Top, U extends Top>() {
function diamondBottom<Bottom extends T | U>() {
var top: Top;
var middle: T | U;
var bottom: Bottom;
top = middle;
middle = bottom;
top = bottom;
}
}
}

View file

@ -0,0 +1,13 @@
function diamondTop<Top>() {
function diamondMiddle<T extends Top, U>() {
function diamondBottom<Bottom extends T | U>() {
var top: Top;
var middle: T | U;
var bottom: Bottom;
top = middle;
middle = bottom;
top = bottom;
}
}
}

View file

@ -0,0 +1,13 @@
function diamondTop<Top>() {
function diamondMiddle<T, U>() {
function diamondBottom<Bottom extends Top | T | U>() {
var top: Top;
var middle: T | U;
var bottom: Bottom;
top = middle;
middle = bottom;
top = bottom;
}
}
}

View file

@ -0,0 +1,13 @@
function diamondTop<Top>() {
function diamondMiddle<T, U>() {
function diamondBottom<Bottom extends Top | T | U>() {
var top: Top;
var middle: Top | T | U;
var bottom: Bottom;
top = middle;
middle = bottom;
top = bottom;
}
}
}

View file

@ -0,0 +1,12 @@
class Animal { run() { } }
class Cat extends Animal { meow }
class Dog extends Animal { woof }
function run(a: Animal) {
a.run();
}
function f<T extends Cat | Dog>(a: T) {
a.run();
run(a);
}

View file

@ -0,0 +1,12 @@
class Animal { run() { } }
class Cat extends Animal { meow }
class Dog extends Animal { woof }
function run(a: Cat | Dog) {
a.run();
}
function f<T extends Cat | Dog>(a: T) {
a.run();
run(a);
}