Skip to content

Commit 2fb9f9b

Browse files
committed
Merge pull request #5919 from Microsoft/port_union_reduction_to_1.7
Port union reduction to 1.7
2 parents a1ff917 + 1c4fd11 commit 2fb9f9b

18 files changed

+1165
-36
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"browserify": "latest",
3636
"istanbul": "latest",
3737
"mocha-fivemat-progress-reporter": "latest",
38-
"tslint": "latest",
38+
"tslint": "~2",
3939
"tsd": "latest"
4040
},
4141
"scripts": {

src/compiler/checker.ts

+57-35
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ namespace ts {
4343
let emptyArray: any[] = [];
4444
let emptySymbols: SymbolTable = {};
4545

46+
let jsxElementClassType: Type = undefined;
47+
4648
let compilerOptions = host.getCompilerOptions();
4749
let languageVersion = compilerOptions.target || ScriptTarget.ES3;
4850
let modulekind = compilerOptions.module ? compilerOptions.module : languageVersion === ScriptTarget.ES6 ? ModuleKind.ES6 : ModuleKind.None;
@@ -4938,9 +4940,6 @@ namespace ts {
49384940
}
49394941
return objectTypeRelatedTo(<ObjectType>source, <ObjectType>target, /*reportErrors*/ false);
49404942
}
4941-
if (source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.TypeParameter) {
4942-
return typeParameterIdenticalTo(<TypeParameter>source, <TypeParameter>target);
4943-
}
49444943
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
49454944
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
49464945
if (result = eachTypeRelatedToSomeType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target)) {
@@ -5071,20 +5070,6 @@ namespace ts {
50715070
return result;
50725071
}
50735072

5074-
function typeParameterIdenticalTo(source: TypeParameter, target: TypeParameter): Ternary {
5075-
if (source.symbol.name !== target.symbol.name) {
5076-
return Ternary.False;
5077-
}
5078-
// covers case when both type parameters does not have constraint (both equal to noConstraintType)
5079-
if (source.constraint === target.constraint) {
5080-
return Ternary.True;
5081-
}
5082-
if (source.constraint === noConstraintType || target.constraint === noConstraintType) {
5083-
return Ternary.False;
5084-
}
5085-
return isIdenticalTo(source.constraint, target.constraint);
5086-
}
5087-
50885073
// Determine if two object types are related by structure. First, check if the result is already available in the global cache.
50895074
// Second, check if we have already started a comparison of the given two types in which case we assume the result to be true.
50905075
// Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are
@@ -5607,27 +5592,20 @@ namespace ts {
56075592
return Ternary.False;
56085593
}
56095594
}
5610-
let result = Ternary.True;
5611-
if (source.typeParameters && target.typeParameters) {
5612-
if (source.typeParameters.length !== target.typeParameters.length) {
5613-
return Ternary.False;
5614-
}
5615-
for (let i = 0, len = source.typeParameters.length; i < len; ++i) {
5616-
let related = compareTypes(source.typeParameters[i], target.typeParameters[i]);
5617-
if (!related) {
5618-
return Ternary.False;
5619-
}
5620-
result &= related;
5621-
}
5622-
}
5623-
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)) {
56245601
return Ternary.False;
56255602
}
56265603
// Spec 1.0 Section 3.8.3 & 3.8.4:
56275604
// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
56285605
source = getErasedSignature(source);
56295606
target = getErasedSignature(target);
5630-
let targetLen = target.parameters.length;
5607+
let result = Ternary.True;
5608+
const targetLen = target.parameters.length;
56315609
for (let i = 0; i < targetLen; i++) {
56325610
let s = isRestParameterIndex(source, i) ? getRestTypeOfSignature(source) : getTypeOfSymbol(source.parameters[i]);
56335611
let t = isRestParameterIndex(target, i) ? getRestTypeOfSignature(target) : getTypeOfSymbol(target.parameters[i]);
@@ -5928,6 +5906,17 @@ namespace ts {
59285906
}
59295907

59305908
function inferFromTypes(source: Type, target: Type) {
5909+
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
5910+
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
5911+
// Source and target are both unions or both intersections. To improve the quality of
5912+
// inferences we first reduce the types by removing constituents that are identically
5913+
// matched by a constituent in the other type. For example, when inferring from
5914+
// 'string | string[]' to 'string | T', we reduce the types to 'string[]' and 'T'.
5915+
const reducedSource = reduceUnionOrIntersectionType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target);
5916+
const reducedTarget = reduceUnionOrIntersectionType(<UnionOrIntersectionType>target, <UnionOrIntersectionType>source);
5917+
source = reducedSource;
5918+
target = reducedTarget;
5919+
}
59315920
if (target.flags & TypeFlags.TypeParameter) {
59325921
// If target is a type parameter, make an inference, unless the source type contains
59335922
// the anyFunctionType (the wildcard type that's used to avoid contextually typing functions).
@@ -5938,8 +5927,7 @@ namespace ts {
59385927
if (source.flags & TypeFlags.ContainsAnyFunctionType) {
59395928
return;
59405929
}
5941-
5942-
let typeParameters = context.typeParameters;
5930+
const typeParameters = context.typeParameters;
59435931
for (let i = 0; i < typeParameters.length; i++) {
59445932
if (target === typeParameters[i]) {
59455933
let inferences = context.inferences[i];
@@ -6086,6 +6074,41 @@ namespace ts {
60866074
}
60876075
}
60886076

6077+
function typeIdenticalToSomeType(source: Type, target: UnionOrIntersectionType): boolean {
6078+
for (const t of target.types) {
6079+
if (isTypeIdenticalTo(source, t)) {
6080+
return true;
6081+
}
6082+
}
6083+
return false;
6084+
}
6085+
6086+
/**
6087+
* Return the reduced form of the source type. This type is computed by by removing all source
6088+
* constituents that have an identical match in the target type.
6089+
*/
6090+
function reduceUnionOrIntersectionType(source: UnionOrIntersectionType, target: UnionOrIntersectionType) {
6091+
let sourceTypes = source.types;
6092+
let sourceIndex = 0;
6093+
let modified = false;
6094+
while (sourceIndex < sourceTypes.length) {
6095+
if (typeIdenticalToSomeType(sourceTypes[sourceIndex], target)) {
6096+
if (!modified) {
6097+
sourceTypes = sourceTypes.slice(0);
6098+
modified = true;
6099+
}
6100+
sourceTypes.splice(sourceIndex, 1);
6101+
}
6102+
else {
6103+
sourceIndex++;
6104+
}
6105+
}
6106+
if (modified) {
6107+
return source.flags & TypeFlags.Union ? getUnionType(sourceTypes, /*noSubtypeReduction*/ true) : getIntersectionType(sourceTypes);
6108+
}
6109+
return source;
6110+
}
6111+
60896112
function getInferenceCandidates(context: InferenceContext, index: number): Type[] {
60906113
let inferences = context.inferences[index];
60916114
return inferences.primary || inferences.secondary || emptyArray;
@@ -7859,7 +7882,6 @@ namespace ts {
78597882
return prop || unknownSymbol;
78607883
}
78617884

7862-
let jsxElementClassType: Type = undefined;
78637885
function getJsxGlobalElementClassType(): Type {
78647886
if (!jsxElementClassType) {
78657887
jsxElementClassType = getExportedTypeFromNamespace(JsxNames.JSX, JsxNames.ElementClass);
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+

0 commit comments

Comments
 (0)