Skip to content

Commit b65a422

Browse files
Fixed contextual typing for tagged template expressions.
1 parent ed9234e commit b65a422

File tree

5 files changed

+255
-7
lines changed

5 files changed

+255
-7
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4813,17 +4813,25 @@ module ts {
48134813
return undefined;
48144814
}
48154815

4816-
// In a typed function call, an argument expression is contextually typed by the type of the corresponding parameter.
4817-
function getContextualTypeForArgument(node: Expression): Type {
4818-
var callExpression = <CallExpression>node.parent;
4819-
var argIndex = indexOf(callExpression.arguments, node);
4816+
// In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter.
4817+
function getContextualTypeForArgument(callTarget: CallLikeExpression, arg: Expression): Type {
4818+
var args = getEffectiveCallArguments(callTarget);
4819+
var argIndex = indexOf(args, arg);
48204820
if (argIndex >= 0) {
4821-
var signature = getResolvedSignature(callExpression);
4821+
var signature = getResolvedSignature(callTarget);
48224822
return getTypeAtPosition(signature, argIndex);
48234823
}
48244824
return undefined;
48254825
}
48264826

4827+
function getContextualTypeForSubstitutionExpression(template: TemplateExpression, substitutionExpression: Expression) {
4828+
if (template.parent.kind === SyntaxKind.TaggedTemplateExpression) {
4829+
return getContextualTypeForArgument(<TaggedTemplateExpression>template.parent, substitutionExpression);
4830+
}
4831+
4832+
return undefined;
4833+
}
4834+
48274835
function getContextualTypeForBinaryOperand(node: Expression): Type {
48284836
var binaryExpression = <BinaryExpression>node.parent;
48294837
var operator = binaryExpression.operator;
@@ -4959,7 +4967,7 @@ module ts {
49594967
return getContextualTypeForReturnExpression(node);
49604968
case SyntaxKind.CallExpression:
49614969
case SyntaxKind.NewExpression:
4962-
return getContextualTypeForArgument(node);
4970+
return getContextualTypeForArgument(<CallExpression>parent, node);
49634971
case SyntaxKind.TypeAssertionExpression:
49644972
return getTypeFromTypeNode((<TypeAssertion>parent).type);
49654973
case SyntaxKind.BinaryExpression:
@@ -4970,6 +4978,11 @@ module ts {
49704978
return getContextualTypeForElementExpression(node);
49714979
case SyntaxKind.ConditionalExpression:
49724980
return getContextualTypeForConditionalOperand(node);
4981+
case SyntaxKind.TemplateExpression:
4982+
return getContextualTypeForSubstitutionExpression(<TemplateExpression>parent.parent, node);
4983+
case SyntaxKind.TemplateSpan:
4984+
Debug.assert(parent.parent.kind === SyntaxKind.TemplateExpression);
4985+
return getContextualTypeForSubstitutionExpression(<TemplateExpression>parent.parent, node);
49734986
}
49744987
return undefined;
49754988
}
@@ -5571,7 +5584,7 @@ module ts {
55715584
}
55725585

55735586
/**
5574-
* Returns the effective arguments for an expression that works like a function invokation.
5587+
* Returns the effective arguments for an expression that works like a function invocation.
55755588
*
55765589
* If 'node' is a CallExpression or a NewExpression, then its argument list is returned.
55775590
* If 'node' is a TaggedTemplateExpression, a new argument list is constructed from the substitution
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//// [taggedTemplateContextualTyping.ts]
2+
3+
function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T;
4+
function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T;
5+
function tempTag1<T>(...rest: any[]): T {
6+
return undefined;
7+
}
8+
9+
tempTag1 `${ x => x }${ 10 }`;
10+
tempTag1 `${ x => x }${ y => y }${ 10 }`;
11+
tempTag1 `${ x => x }${ (y: number) => y }${ undefined }`;
12+
tempTag1 `${ (x: number) => x }${ y => y }${ undefined }`;
13+
14+
function tempTag2(templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number;
15+
function tempTag2(templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string;
16+
function tempTag2(...rest: any[]): any {
17+
return undefined;
18+
}
19+
20+
tempTag2 `${ x => x }${ 0 }`;
21+
tempTag2 `${ x => x }${ y => y }${ "hello" }`;
22+
tempTag2 `${ x => x }${ 0 }`;
23+
24+
//// [taggedTemplateContextualTyping.js]
25+
function tempTag1() {
26+
var rest = [];
27+
for (var _i = 0; _i < arguments.length; _i++) {
28+
rest[_i - 0] = arguments[_i];
29+
}
30+
return undefined;
31+
}
32+
tempTag1 `${function (x) { return x; }}${10}`;
33+
tempTag1 `${function (x) { return x; }}${function (y) { return y; }}${10}`;
34+
tempTag1 `${function (x) { return x; }}${function (y) { return y; }}${undefined}`;
35+
tempTag1 `${function (x) { return x; }}${function (y) { return y; }}${undefined}`;
36+
function tempTag2() {
37+
var rest = [];
38+
for (var _i = 0; _i < arguments.length; _i++) {
39+
rest[_i - 0] = arguments[_i];
40+
}
41+
return undefined;
42+
}
43+
tempTag2 `${function (x) { return x; }}${0}`;
44+
tempTag2 `${function (x) { return x; }}${function (y) { return y; }}${"hello"}`;
45+
tempTag2 `${function (x) { return x; }}${0}`;
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
=== tests/cases/conformance/expressions/contextualTyping/taggedTemplateContextualTyping.ts ===
2+
3+
function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T;
4+
>tempTag1 : { <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T; <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T; }
5+
>T : T
6+
>templateStrs : TemplateStringsArray
7+
>TemplateStringsArray : TemplateStringsArray
8+
>f : (x: T) => T
9+
>x : T
10+
>T : T
11+
>T : T
12+
>x : T
13+
>T : T
14+
>T : T
15+
16+
function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T;
17+
>tempTag1 : { <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T; <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T; }
18+
>T : T
19+
>templateStrs : TemplateStringsArray
20+
>TemplateStringsArray : TemplateStringsArray
21+
>f : (x: T) => T
22+
>x : T
23+
>T : T
24+
>T : T
25+
>h : (y: T) => T
26+
>y : T
27+
>T : T
28+
>T : T
29+
>x : T
30+
>T : T
31+
>T : T
32+
33+
function tempTag1<T>(...rest: any[]): T {
34+
>tempTag1 : { <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T; <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T; }
35+
>T : T
36+
>rest : any[]
37+
>T : T
38+
39+
return undefined;
40+
>undefined : undefined
41+
}
42+
43+
tempTag1 `${ x => x }${ 10 }`;
44+
>tempTag1 : { <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T; <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T; }
45+
>x => x : (x: number) => number
46+
>x : number
47+
>x : number
48+
49+
tempTag1 `${ x => x }${ y => y }${ 10 }`;
50+
>tempTag1 : { <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T; <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T; }
51+
>x => x : (x: number) => number
52+
>x : number
53+
>x : number
54+
>y => y : (y: number) => number
55+
>y : number
56+
>y : number
57+
58+
tempTag1 `${ x => x }${ (y: number) => y }${ undefined }`;
59+
>tempTag1 : { <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T; <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T; }
60+
>x => x : (x: number) => number
61+
>x : number
62+
>x : number
63+
>(y: number) => y : (y: number) => number
64+
>y : number
65+
>y : number
66+
>undefined : undefined
67+
68+
tempTag1 `${ (x: number) => x }${ y => y }${ undefined }`;
69+
>tempTag1 : { <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T; <T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T; }
70+
>(x: number) => x : (x: number) => number
71+
>x : number
72+
>x : number
73+
>y => y : (y: number) => number
74+
>y : number
75+
>y : number
76+
>undefined : undefined
77+
78+
function tempTag2(templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number;
79+
>tempTag2 : { (templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number; (templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string; }
80+
>templateStrs : TemplateStringsArray
81+
>TemplateStringsArray : TemplateStringsArray
82+
>f : (x: number) => number
83+
>x : number
84+
>x : number
85+
86+
function tempTag2(templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string;
87+
>tempTag2 : { (templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number; (templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string; }
88+
>templateStrs : TemplateStringsArray
89+
>TemplateStringsArray : TemplateStringsArray
90+
>f : (x: string) => string
91+
>x : string
92+
>h : (y: string) => string
93+
>y : string
94+
>x : string
95+
96+
function tempTag2(...rest: any[]): any {
97+
>tempTag2 : { (templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number; (templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string; }
98+
>rest : any[]
99+
100+
return undefined;
101+
>undefined : undefined
102+
}
103+
104+
tempTag2 `${ x => x }${ 0 }`;
105+
>tempTag2 : { (templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number; (templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string; }
106+
>x => x : (x: number) => number
107+
>x : number
108+
>x : number
109+
110+
tempTag2 `${ x => x }${ y => y }${ "hello" }`;
111+
>tempTag2 : { (templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number; (templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string; }
112+
>x => x : (x: string) => string
113+
>x : string
114+
>x : string
115+
>y => y : (y: string) => string
116+
>y : string
117+
>y : string
118+
119+
tempTag2 `${ x => x }${ 0 }`;
120+
>tempTag2 : { (templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number; (templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string; }
121+
>x => x : (x: number) => number
122+
>x : number
123+
>x : number
124+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// @target: ES6
2+
3+
function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T;
4+
function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T;
5+
function tempTag1<T>(...rest: any[]): T {
6+
return undefined;
7+
}
8+
9+
tempTag1 `${ x => x }${ 10 }`;
10+
tempTag1 `${ x => x }${ y => y }${ 10 }`;
11+
tempTag1 `${ x => x }${ (y: number) => y }${ undefined }`;
12+
tempTag1 `${ (x: number) => x }${ y => y }${ undefined }`;
13+
14+
function tempTag2(templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number;
15+
function tempTag2(templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string;
16+
function tempTag2(...rest: any[]): any {
17+
return undefined;
18+
}
19+
20+
tempTag2 `${ x => x }${ 0 }`;
21+
tempTag2 `${ x => x }${ y => y }${ "hello" }`;
22+
tempTag2 `${ x => x }${ 0 }`;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T;
4+
////function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T;
5+
////function tempTag1<T>(...rest: any[]): T {
6+
//// return undefined;
7+
////}
8+
////
9+
////tempTag1 `${ x => /*0*/x }${ 10 }`;
10+
////tempTag1 `${ x => /*1*/x }${ x => /*2*/x }${ 10 }`;
11+
////tempTag1 `${ x => /*3*/x }${ (x: number) => /*4*/x }${ undefined }`;
12+
////tempTag1 `${ (x: number) => /*5*/x }${ x => /*6*/x }${ undefined }`;
13+
////
14+
////function tempTag2(templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number;
15+
////function tempTag2(templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string;
16+
////function tempTag2(...rest: any[]): any {
17+
//// return undefined;
18+
////}
19+
////
20+
////tempTag2 `${ x => /*7*/x }${ 0 }`;
21+
////tempTag2 `${ x => /*8*/x }${ undefined }`;
22+
////tempTag2 `${ x => /*9*/x }${ x => /*10*/x }${ "hello" }`;
23+
////tempTag2 `${ x => /*11*/x }${ undefined }${ "hello" }`;
24+
25+
// The first group of parameters, [0, 8], should all be contextually typed as 'number'.
26+
// The second group, [9, 11], should be typed as 'string'.
27+
var numTypedVariableCount = 9;
28+
var strTypedVariableCount = 3;
29+
30+
var markers = test.markers();
31+
32+
if (numTypedVariableCount + strTypedVariableCount !== markers.length) {
33+
throw "Unexpected number of markers in file.";
34+
}
35+
36+
for (var i = 0; i < numTypedVariableCount; i++) {
37+
goTo.marker("" + i);
38+
verify.quickInfoIs("(parameter) x: number");
39+
}
40+
41+
for (var i = 0; i < strTypedVariableCount; i++) {
42+
goTo.marker("" + (i + numTypedVariableCount));
43+
verify.quickInfoIs("(parameter) x: string");
44+
}

0 commit comments

Comments
 (0)