Skip to content

Commit ccb4326

Browse files
ahejlsbergvladima
authored andcommitted
Merge pull request #5895 from Microsoft/unionTypeParameterInference
Fix union type parameter inference Conflicts: src/compiler/checker.ts
1 parent b022893 commit ccb4326

9 files changed

+255
-32
lines changed

src/compiler/checker.ts

+8-32
Original file line numberDiff line numberDiff line change
@@ -4940,9 +4940,6 @@ namespace ts {
49404940
}
49414941
return objectTypeRelatedTo(<ObjectType>source, <ObjectType>target, /*reportErrors*/ false);
49424942
}
4943-
if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) {
4944-
return typeParameterIdenticalTo(<TypeParameter>source, <TypeParameter>target);
4945-
}
49464943
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
49474944
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
49484945
if (result = eachTypeRelatedToSomeType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target)) {
@@ -5073,20 +5070,6 @@ namespace ts {
50735070
return result;
50745071
}
50755072

5076-
function typeParameterIdenticalTo(source: TypeParameter, target: TypeParameter): Ternary {
5077-
if (source.symbol.name !== target.symbol.name) {
5078-
return Ternary.False;
5079-
}
5080-
// covers case when both type parameters does not have constraint (both equal to noConstraintType)
5081-
if (source.constraint === target.constraint) {
5082-
return Ternary.True;
5083-
}
5084-
if (source.constraint === noConstraintType || target.constraint === noConstraintType) {
5085-
return Ternary.False;
5086-
}
5087-
return isIdenticalTo(source.constraint, target.constraint);
5088-
}
5089-
50905073
// Determine if two object types are related by structure. First, check if the result is already available in the global cache.
50915074
// Second, check if we have already started a comparison of the given two types in which case we assume the result to be true.
50925075
// Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are
@@ -5609,27 +5592,20 @@ namespace ts {
56095592
return Ternary.False;
56105593
}
56115594
}
5612-
let result = Ternary.True;
5613-
if (source.typeParameters && target.typeParameters) {
5614-
if (source.typeParameters.length !== target.typeParameters.length) {
5615-
return Ternary.False;
5616-
}
5617-
for (let i = 0, len = source.typeParameters.length; i < len; ++i) {
5618-
let related = compareTypes(source.typeParameters[i], target.typeParameters[i]);
5619-
if (!related) {
5620-
return Ternary.False;
5621-
}
5622-
result &= related;
5623-
}
5624-
}
5625-
else if (source.typeParameters || target.typeParameters) {
5595+
// Check that the two signatures have the same number of type parameters. We might consider
5596+
// also checking that any type parameter constraints match, but that would require instantiating
5597+
// the constraints with a common set of type arguments to get relatable entities in places where
5598+
// type parameters occur in the constraints. The complexity of doing that doesn't seem worthwhile,
5599+
// particularly as we're comparing erased versions of the signatures below.
5600+
if ((source.typeParameters ? source.typeParameters.length : 0) !== (target.typeParameters ? target.typeParameters.length : 0)) {
56265601
return Ternary.False;
56275602
}
56285603
// Spec 1.0 Section 3.8.3 & 3.8.4:
56295604
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
56305605
source = getErasedSignature(source);
56315606
target = getErasedSignature(target);
5632-
let targetLen = target.parameters.length;
5607+
let result = Ternary.True;
5608+
const targetLen = target.parameters.length;
56335609
for (let i = 0; i < targetLen; i++) {
56345610
let s = isRestParameterIndex(source, i) ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]);
56355611
let t = isRestParameterIndex(target, i) ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//// [genericSignatureIdentity.ts]
2+
// This test is here to remind us of our current limits of type identity checking.
3+
// Ideally all of the below declarations would be considered different (and thus errors)
4+
// but they aren't because we erase type parameters to type any and don't check that
5+
// constraints are identical.
6+
7+
var x: {
8+
<T extends Date>(x: T): T;
9+
};
10+
11+
var x: {
12+
<T extends number>(x: T): T;
13+
};
14+
15+
var x: {
16+
<T>(x: T): T;
17+
};
18+
19+
var x: {
20+
<T>(x: any): any;
21+
};
22+
23+
24+
//// [genericSignatureIdentity.js]
25+
// This test is here to remind us of our current limits of type identity checking.
26+
// Ideally all of the below declarations would be considered different (and thus errors)
27+
// but they aren't because we erase type parameters to type any and don't check that
28+
// constraints are identical.
29+
var x;
30+
var x;
31+
var x;
32+
var x;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
=== tests/cases/compiler/genericSignatureIdentity.ts ===
2+
// This test is here to remind us of our current limits of type identity checking.
3+
// Ideally all of the below declarations would be considered different (and thus errors)
4+
// but they aren't because we erase type parameters to type any and don't check that
5+
// constraints are identical.
6+
7+
var x: {
8+
>x : Symbol(x, Decl(genericSignatureIdentity.ts, 5, 3), Decl(genericSignatureIdentity.ts, 9, 3), Decl(genericSignatureIdentity.ts, 13, 3), Decl(genericSignatureIdentity.ts, 17, 3))
9+
10+
<T extends Date>(x: T): T;
11+
>T : Symbol(T, Decl(genericSignatureIdentity.ts, 6, 5))
12+
>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
13+
>x : Symbol(x, Decl(genericSignatureIdentity.ts, 6, 21))
14+
>T : Symbol(T, Decl(genericSignatureIdentity.ts, 6, 5))
15+
>T : Symbol(T, Decl(genericSignatureIdentity.ts, 6, 5))
16+
17+
};
18+
19+
var x: {
20+
>x : Symbol(x, Decl(genericSignatureIdentity.ts, 5, 3), Decl(genericSignatureIdentity.ts, 9, 3), Decl(genericSignatureIdentity.ts, 13, 3), Decl(genericSignatureIdentity.ts, 17, 3))
21+
22+
<T extends number>(x: T): T;
23+
>T : Symbol(T, Decl(genericSignatureIdentity.ts, 10, 5))
24+
>x : Symbol(x, Decl(genericSignatureIdentity.ts, 10, 23))
25+
>T : Symbol(T, Decl(genericSignatureIdentity.ts, 10, 5))
26+
>T : Symbol(T, Decl(genericSignatureIdentity.ts, 10, 5))
27+
28+
};
29+
30+
var x: {
31+
>x : Symbol(x, Decl(genericSignatureIdentity.ts, 5, 3), Decl(genericSignatureIdentity.ts, 9, 3), Decl(genericSignatureIdentity.ts, 13, 3), Decl(genericSignatureIdentity.ts, 17, 3))
32+
33+
<T>(x: T): T;
34+
>T : Symbol(T, Decl(genericSignatureIdentity.ts, 14, 5))
35+
>x : Symbol(x, Decl(genericSignatureIdentity.ts, 14, 8))
36+
>T : Symbol(T, Decl(genericSignatureIdentity.ts, 14, 5))
37+
>T : Symbol(T, Decl(genericSignatureIdentity.ts, 14, 5))
38+
39+
};
40+
41+
var x: {
42+
>x : Symbol(x, Decl(genericSignatureIdentity.ts, 5, 3), Decl(genericSignatureIdentity.ts, 9, 3), Decl(genericSignatureIdentity.ts, 13, 3), Decl(genericSignatureIdentity.ts, 17, 3))
43+
44+
<T>(x: any): any;
45+
>T : Symbol(T, Decl(genericSignatureIdentity.ts, 18, 5))
46+
>x : Symbol(x, Decl(genericSignatureIdentity.ts, 18, 8))
47+
48+
};
49+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
=== tests/cases/compiler/genericSignatureIdentity.ts ===
2+
// This test is here to remind us of our current limits of type identity checking.
3+
// Ideally all of the below declarations would be considered different (and thus errors)
4+
// but they aren't because we erase type parameters to type any and don't check that
5+
// constraints are identical.
6+
7+
var x: {
8+
>x : <T extends Date>(x: T) => T
9+
10+
<T extends Date>(x: T): T;
11+
>T : T
12+
>Date : Date
13+
>x : T
14+
>T : T
15+
>T : T
16+
17+
};
18+
19+
var x: {
20+
>x : <T extends Date>(x: T) => T
21+
22+
<T extends number>(x: T): T;
23+
>T : T
24+
>x : T
25+
>T : T
26+
>T : T
27+
28+
};
29+
30+
var x: {
31+
>x : <T extends Date>(x: T) => T
32+
33+
<T>(x: T): T;
34+
>T : T
35+
>x : T
36+
>T : T
37+
>T : T
38+
39+
};
40+
41+
var x: {
42+
>x : <T extends Date>(x: T) => T
43+
44+
<T>(x: any): any;
45+
>T : T
46+
>x : any
47+
48+
};
49+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [unionTypeParameterInference.ts]
2+
// Regression test for #5861
3+
4+
interface Foo<T> { prop: T; }
5+
6+
declare function lift<U>(value: U | Foo<U>): Foo<U>;
7+
8+
function unlift<U>(value: U | Foo<U>): U {
9+
return lift(value).prop;
10+
}
11+
12+
13+
//// [unionTypeParameterInference.js]
14+
// Regression test for #5861
15+
function unlift(value) {
16+
return lift(value).prop;
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/compiler/unionTypeParameterInference.ts ===
2+
// Regression test for #5861
3+
4+
interface Foo<T> { prop: T; }
5+
>Foo : Symbol(Foo, Decl(unionTypeParameterInference.ts, 0, 0))
6+
>T : Symbol(T, Decl(unionTypeParameterInference.ts, 2, 14))
7+
>prop : Symbol(prop, Decl(unionTypeParameterInference.ts, 2, 18))
8+
>T : Symbol(T, Decl(unionTypeParameterInference.ts, 2, 14))
9+
10+
declare function lift<U>(value: U | Foo<U>): Foo<U>;
11+
>lift : Symbol(lift, Decl(unionTypeParameterInference.ts, 2, 29))
12+
>U : Symbol(U, Decl(unionTypeParameterInference.ts, 4, 22))
13+
>value : Symbol(value, Decl(unionTypeParameterInference.ts, 4, 25))
14+
>U : Symbol(U, Decl(unionTypeParameterInference.ts, 4, 22))
15+
>Foo : Symbol(Foo, Decl(unionTypeParameterInference.ts, 0, 0))
16+
>U : Symbol(U, Decl(unionTypeParameterInference.ts, 4, 22))
17+
>Foo : Symbol(Foo, Decl(unionTypeParameterInference.ts, 0, 0))
18+
>U : Symbol(U, Decl(unionTypeParameterInference.ts, 4, 22))
19+
20+
function unlift<U>(value: U | Foo<U>): U {
21+
>unlift : Symbol(unlift, Decl(unionTypeParameterInference.ts, 4, 52))
22+
>U : Symbol(U, Decl(unionTypeParameterInference.ts, 6, 16))
23+
>value : Symbol(value, Decl(unionTypeParameterInference.ts, 6, 19))
24+
>U : Symbol(U, Decl(unionTypeParameterInference.ts, 6, 16))
25+
>Foo : Symbol(Foo, Decl(unionTypeParameterInference.ts, 0, 0))
26+
>U : Symbol(U, Decl(unionTypeParameterInference.ts, 6, 16))
27+
>U : Symbol(U, Decl(unionTypeParameterInference.ts, 6, 16))
28+
29+
return lift(value).prop;
30+
>lift(value).prop : Symbol(Foo.prop, Decl(unionTypeParameterInference.ts, 2, 18))
31+
>lift : Symbol(lift, Decl(unionTypeParameterInference.ts, 2, 29))
32+
>value : Symbol(value, Decl(unionTypeParameterInference.ts, 6, 19))
33+
>prop : Symbol(Foo.prop, Decl(unionTypeParameterInference.ts, 2, 18))
34+
}
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
=== tests/cases/compiler/unionTypeParameterInference.ts ===
2+
// Regression test for #5861
3+
4+
interface Foo<T> { prop: T; }
5+
>Foo : Foo<T>
6+
>T : T
7+
>prop : T
8+
>T : T
9+
10+
declare function lift<U>(value: U | Foo<U>): Foo<U>;
11+
>lift : <U>(value: U | Foo<U>) => Foo<U>
12+
>U : U
13+
>value : U | Foo<U>
14+
>U : U
15+
>Foo : Foo<T>
16+
>U : U
17+
>Foo : Foo<T>
18+
>U : U
19+
20+
function unlift<U>(value: U | Foo<U>): U {
21+
>unlift : <U>(value: U | Foo<U>) => U
22+
>U : U
23+
>value : U | Foo<U>
24+
>U : U
25+
>Foo : Foo<T>
26+
>U : U
27+
>U : U
28+
29+
return lift(value).prop;
30+
>lift(value).prop : U
31+
>lift(value) : Foo<U>
32+
>lift : <U>(value: U | Foo<U>) => Foo<U>
33+
>value : U | Foo<U>
34+
>prop : U
35+
}
36+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// This test is here to remind us of our current limits of type identity checking.
2+
// Ideally all of the below declarations would be considered different (and thus errors)
3+
// but they aren't because we erase type parameters to type any and don't check that
4+
// constraints are identical.
5+
6+
var x: {
7+
<T extends Date>(x: T): T;
8+
};
9+
10+
var x: {
11+
<T extends number>(x: T): T;
12+
};
13+
14+
var x: {
15+
<T>(x: T): T;
16+
};
17+
18+
var x: {
19+
<T>(x: any): any;
20+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Regression test for #5861
2+
3+
interface Foo<T> { prop: T; }
4+
5+
declare function lift<U>(value: U | Foo<U>): Foo<U>;
6+
7+
function unlift<U>(value: U | Foo<U>): U {
8+
return lift(value).prop;
9+
}

0 commit comments

Comments
 (0)