Skip to content

Commit e96983d

Browse files
committed
Merge pull request #908 from Microsoft/contextualSignatureUnionTypes
Union types in contextual signature instantiations
2 parents ad6203f + e5872b4 commit e96983d

File tree

5 files changed

+228
-7
lines changed

5 files changed

+228
-7
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3758,11 +3758,12 @@ module ts {
37583758
}
37593759
}
37603760

3761-
function createInferenceContext(typeParameters: TypeParameter[]): InferenceContext {
3761+
function createInferenceContext(typeParameters: TypeParameter[], inferUnionTypes: boolean): InferenceContext {
37623762
var inferences: Type[][] = [];
37633763
for (var i = 0; i < typeParameters.length; i++) inferences.push([]);
37643764
return {
37653765
typeParameters: typeParameters,
3766+
inferUnionTypes: inferUnionTypes,
37663767
inferenceCount: 0,
37673768
inferences: inferences,
37683769
inferredTypes: new Array(typeParameters.length),
@@ -3909,10 +3910,9 @@ module ts {
39093910
if (!result) {
39103911
var inferences = context.inferences[index];
39113912
if (inferences.length) {
3912-
// Find type that is supertype of all others
3913-
var supertype = getCommonSupertype(inferences);
3914-
// Infer widened supertype, or the undefined type for no common supertype
3915-
var inferredType = supertype ? getWidenedType(supertype) : undefinedType;
3913+
// Infer widened union or supertype, or the undefined type for no common supertype
3914+
var unionOrSuperType = context.inferUnionTypes ? getUnionType(inferences) : getCommonSupertype(inferences);
3915+
var inferredType = unionOrSuperType ? getWidenedType(unionOrSuperType) : undefinedType;
39163916
}
39173917
else {
39183918
// Infer the empty object type when no inferences were made
@@ -4976,7 +4976,7 @@ module ts {
49764976

49774977
// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
49784978
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper: TypeMapper): Signature {
4979-
var context = createInferenceContext(signature.typeParameters);
4979+
var context = createInferenceContext(signature.typeParameters, /*inferUnionTypes*/ true);
49804980
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
49814981
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
49824982
inferTypes(context, instantiateType(source, contextualMapper), target);
@@ -4986,7 +4986,7 @@ module ts {
49864986

49874987
function inferTypeArguments(signature: Signature, args: Expression[], excludeArgument?: boolean[]): Type[] {
49884988
var typeParameters = signature.typeParameters;
4989-
var context = createInferenceContext(typeParameters);
4989+
var context = createInferenceContext(typeParameters, /*inferUnionTypes*/ false);
49904990
var mapper = createInferenceMapper(context);
49914991
// First infer from arguments that are not context sensitive
49924992
for (var i = 0; i < args.length; i++) {

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,7 @@ module ts {
10101010

10111011
export interface InferenceContext {
10121012
typeParameters: TypeParameter[]; // Type parameters for which inferences are made
1013+
inferUnionTypes: boolean; // Infer union types for disjoint candidates (otherwise undefinedType)
10131014
inferenceCount: number; // Incremented for every inference made (whether new or not)
10141015
inferences: Type[][]; // Inferences made for each type parameter
10151016
inferredTypes: Type[]; // Inferred type for each type parameter
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//// [contextualSignatureInstantiation.ts]
2+
// TypeScript Spec, section 4.12.2:
3+
// If e is an expression of a function type that contains exactly one generic call signature and no other members,
4+
// and T is a function type with exactly one non - generic call signature and no other members, then any inferences
5+
// made for type parameters referenced by the parameters of T's call signature are fixed, and e's type is changed
6+
// to a function type with e's call signature instantiated in the context of T's call signature (section 3.8.5).
7+
8+
declare function foo<T>(cb: (x: number, y: string) => T): T;
9+
declare function bar<T, U, V>(x: T, y: U, cb: (x: T, y: U) => V): V;
10+
declare function baz<T, U>(x: T, y: T, cb: (x: T, y: T) => U): U;
11+
12+
declare function g<T>(x: T, y: T): T;
13+
declare function h<T, U>(x: T, y: U): T[] | U[];
14+
15+
var a: number;
16+
var a = bar(1, 1, g); // Should be number
17+
var a = baz(1, 1, g); // Should be number
18+
19+
var b: number | string;
20+
var b = foo(g); // Should be number | string
21+
var b = bar(1, "one", g); // Should be number | string
22+
var b = bar("one", 1, g); // Should be number | string
23+
var b = baz(b, b, g); // Should be number | string
24+
25+
var d: number[] | string[];
26+
var d = foo(h); // Should be number[] | string[]
27+
var d = bar(1, "one", h); // Should be number[] | string[]
28+
var d = bar("one", 1, h); // Should be number[] | string[]
29+
var d = baz(d, d, g); // Should be number[] | string[]
30+
31+
32+
//// [contextualSignatureInstantiation.js]
33+
// TypeScript Spec, section 4.12.2:
34+
// If e is an expression of a function type that contains exactly one generic call signature and no other members,
35+
// and T is a function type with exactly one non - generic call signature and no other members, then any inferences
36+
// made for type parameters referenced by the parameters of T's call signature are fixed, and e's type is changed
37+
// to a function type with e's call signature instantiated in the context of T's call signature (section 3.8.5).
38+
var a;
39+
var a = bar(1, 1, g); // Should be number
40+
var a = baz(1, 1, g); // Should be number
41+
var b;
42+
var b = foo(g); // Should be number | string
43+
var b = bar(1, "one", g); // Should be number | string
44+
var b = bar("one", 1, g); // Should be number | string
45+
var b = baz(b, b, g); // Should be number | string
46+
var d;
47+
var d = foo(h); // Should be number[] | string[]
48+
var d = bar(1, "one", h); // Should be number[] | string[]
49+
var d = bar("one", 1, h); // Should be number[] | string[]
50+
var d = baz(d, d, g); // Should be number[] | string[]
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
=== tests/cases/conformance/types/typeRelationships/typeInference/contextualSignatureInstantiation.ts ===
2+
// TypeScript Spec, section 4.12.2:
3+
// If e is an expression of a function type that contains exactly one generic call signature and no other members,
4+
// and T is a function type with exactly one non - generic call signature and no other members, then any inferences
5+
// made for type parameters referenced by the parameters of T's call signature are fixed, and e's type is changed
6+
// to a function type with e's call signature instantiated in the context of T's call signature (section 3.8.5).
7+
8+
declare function foo<T>(cb: (x: number, y: string) => T): T;
9+
>foo : <T>(cb: (x: number, y: string) => T) => T
10+
>T : T
11+
>cb : (x: number, y: string) => T
12+
>x : number
13+
>y : string
14+
>T : T
15+
>T : T
16+
17+
declare function bar<T, U, V>(x: T, y: U, cb: (x: T, y: U) => V): V;
18+
>bar : <T, U, V>(x: T, y: U, cb: (x: T, y: U) => V) => V
19+
>T : T
20+
>U : U
21+
>V : V
22+
>x : T
23+
>T : T
24+
>y : U
25+
>U : U
26+
>cb : (x: T, y: U) => V
27+
>x : T
28+
>T : T
29+
>y : U
30+
>U : U
31+
>V : V
32+
>V : V
33+
34+
declare function baz<T, U>(x: T, y: T, cb: (x: T, y: T) => U): U;
35+
>baz : <T, U>(x: T, y: T, cb: (x: T, y: T) => U) => U
36+
>T : T
37+
>U : U
38+
>x : T
39+
>T : T
40+
>y : T
41+
>T : T
42+
>cb : (x: T, y: T) => U
43+
>x : T
44+
>T : T
45+
>y : T
46+
>T : T
47+
>U : U
48+
>U : U
49+
50+
declare function g<T>(x: T, y: T): T;
51+
>g : <T>(x: T, y: T) => T
52+
>T : T
53+
>x : T
54+
>T : T
55+
>y : T
56+
>T : T
57+
>T : T
58+
59+
declare function h<T, U>(x: T, y: U): T[] | U[];
60+
>h : <T, U>(x: T, y: U) => T[] | U[]
61+
>T : T
62+
>U : U
63+
>x : T
64+
>T : T
65+
>y : U
66+
>U : U
67+
>T : T
68+
>U : U
69+
70+
var a: number;
71+
>a : number
72+
73+
var a = bar(1, 1, g); // Should be number
74+
>a : number
75+
>bar(1, 1, g) : number
76+
>bar : <T, U, V>(x: T, y: U, cb: (x: T, y: U) => V) => V
77+
>g : <T>(x: T, y: T) => T
78+
79+
var a = baz(1, 1, g); // Should be number
80+
>a : number
81+
>baz(1, 1, g) : number
82+
>baz : <T, U>(x: T, y: T, cb: (x: T, y: T) => U) => U
83+
>g : <T>(x: T, y: T) => T
84+
85+
var b: number | string;
86+
>b : string | number
87+
88+
var b = foo(g); // Should be number | string
89+
>b : string | number
90+
>foo(g) : string | number
91+
>foo : <T>(cb: (x: number, y: string) => T) => T
92+
>g : <T>(x: T, y: T) => T
93+
94+
var b = bar(1, "one", g); // Should be number | string
95+
>b : string | number
96+
>bar(1, "one", g) : string | number
97+
>bar : <T, U, V>(x: T, y: U, cb: (x: T, y: U) => V) => V
98+
>g : <T>(x: T, y: T) => T
99+
100+
var b = bar("one", 1, g); // Should be number | string
101+
>b : string | number
102+
>bar("one", 1, g) : string | number
103+
>bar : <T, U, V>(x: T, y: U, cb: (x: T, y: U) => V) => V
104+
>g : <T>(x: T, y: T) => T
105+
106+
var b = baz(b, b, g); // Should be number | string
107+
>b : string | number
108+
>baz(b, b, g) : string | number
109+
>baz : <T, U>(x: T, y: T, cb: (x: T, y: T) => U) => U
110+
>b : string | number
111+
>b : string | number
112+
>g : <T>(x: T, y: T) => T
113+
114+
var d: number[] | string[];
115+
>d : string[] | number[]
116+
117+
var d = foo(h); // Should be number[] | string[]
118+
>d : string[] | number[]
119+
>foo(h) : string[] | number[]
120+
>foo : <T>(cb: (x: number, y: string) => T) => T
121+
>h : <T, U>(x: T, y: U) => T[] | U[]
122+
123+
var d = bar(1, "one", h); // Should be number[] | string[]
124+
>d : string[] | number[]
125+
>bar(1, "one", h) : string[] | number[]
126+
>bar : <T, U, V>(x: T, y: U, cb: (x: T, y: U) => V) => V
127+
>h : <T, U>(x: T, y: U) => T[] | U[]
128+
129+
var d = bar("one", 1, h); // Should be number[] | string[]
130+
>d : string[] | number[]
131+
>bar("one", 1, h) : string[] | number[]
132+
>bar : <T, U, V>(x: T, y: U, cb: (x: T, y: U) => V) => V
133+
>h : <T, U>(x: T, y: U) => T[] | U[]
134+
135+
var d = baz(d, d, g); // Should be number[] | string[]
136+
>d : string[] | number[]
137+
>baz(d, d, g) : string[] | number[]
138+
>baz : <T, U>(x: T, y: T, cb: (x: T, y: T) => U) => U
139+
>d : string[] | number[]
140+
>d : string[] | number[]
141+
>g : <T>(x: T, y: T) => T
142+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// TypeScript Spec, section 4.12.2:
2+
// If e is an expression of a function type that contains exactly one generic call signature and no other members,
3+
// and T is a function type with exactly one non - generic call signature and no other members, then any inferences
4+
// made for type parameters referenced by the parameters of T's call signature are fixed, and e's type is changed
5+
// to a function type with e's call signature instantiated in the context of T's call signature (section 3.8.5).
6+
7+
declare function foo<T>(cb: (x: number, y: string) => T): T;
8+
declare function bar<T, U, V>(x: T, y: U, cb: (x: T, y: U) => V): V;
9+
declare function baz<T, U>(x: T, y: T, cb: (x: T, y: T) => U): U;
10+
11+
declare function g<T>(x: T, y: T): T;
12+
declare function h<T, U>(x: T, y: U): T[] | U[];
13+
14+
var a: number;
15+
var a = bar(1, 1, g); // Should be number
16+
var a = baz(1, 1, g); // Should be number
17+
18+
var b: number | string;
19+
var b = foo(g); // Should be number | string
20+
var b = bar(1, "one", g); // Should be number | string
21+
var b = bar("one", 1, g); // Should be number | string
22+
var b = baz(b, b, g); // Should be number | string
23+
24+
var d: number[] | string[];
25+
var d = foo(h); // Should be number[] | string[]
26+
var d = bar(1, "one", h); // Should be number[] | string[]
27+
var d = bar("one", 1, h); // Should be number[] | string[]
28+
var d = baz(d, d, g); // Should be number[] | string[]

0 commit comments

Comments
 (0)