Fixed contextual typing for tagged template expressions.

This commit is contained in:
Daniel Rosenwasser 2014-12-15 14:11:21 -08:00
parent ed9234ed32
commit b65a422c7a
5 changed files with 255 additions and 7 deletions

View File

@ -4813,17 +4813,25 @@ module ts {
return undefined;
}
// In a typed function call, an argument expression is contextually typed by the type of the corresponding parameter.
function getContextualTypeForArgument(node: Expression): Type {
var callExpression = <CallExpression>node.parent;
var argIndex = indexOf(callExpression.arguments, node);
// In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter.
function getContextualTypeForArgument(callTarget: CallLikeExpression, arg: Expression): Type {
var args = getEffectiveCallArguments(callTarget);
var argIndex = indexOf(args, arg);
if (argIndex >= 0) {
var signature = getResolvedSignature(callExpression);
var signature = getResolvedSignature(callTarget);
return getTypeAtPosition(signature, argIndex);
}
return undefined;
}
function getContextualTypeForSubstitutionExpression(template: TemplateExpression, substitutionExpression: Expression) {
if (template.parent.kind === SyntaxKind.TaggedTemplateExpression) {
return getContextualTypeForArgument(<TaggedTemplateExpression>template.parent, substitutionExpression);
}
return undefined;
}
function getContextualTypeForBinaryOperand(node: Expression): Type {
var binaryExpression = <BinaryExpression>node.parent;
var operator = binaryExpression.operator;
@ -4959,7 +4967,7 @@ module ts {
return getContextualTypeForReturnExpression(node);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return getContextualTypeForArgument(node);
return getContextualTypeForArgument(<CallExpression>parent, node);
case SyntaxKind.TypeAssertionExpression:
return getTypeFromTypeNode((<TypeAssertion>parent).type);
case SyntaxKind.BinaryExpression:
@ -4970,6 +4978,11 @@ module ts {
return getContextualTypeForElementExpression(node);
case SyntaxKind.ConditionalExpression:
return getContextualTypeForConditionalOperand(node);
case SyntaxKind.TemplateExpression:
return getContextualTypeForSubstitutionExpression(<TemplateExpression>parent.parent, node);
case SyntaxKind.TemplateSpan:
Debug.assert(parent.parent.kind === SyntaxKind.TemplateExpression);
return getContextualTypeForSubstitutionExpression(<TemplateExpression>parent.parent, node);
}
return undefined;
}
@ -5571,7 +5584,7 @@ module ts {
}
/**
* Returns the effective arguments for an expression that works like a function invokation.
* Returns the effective arguments for an expression that works like a function invocation.
*
* If 'node' is a CallExpression or a NewExpression, then its argument list is returned.
* If 'node' is a TaggedTemplateExpression, a new argument list is constructed from the substitution

View File

@ -0,0 +1,45 @@
//// [taggedTemplateContextualTyping.ts]
function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T;
function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T;
function tempTag1<T>(...rest: any[]): T {
return undefined;
}
tempTag1 `${ x => x }${ 10 }`;
tempTag1 `${ x => x }${ y => y }${ 10 }`;
tempTag1 `${ x => x }${ (y: number) => y }${ undefined }`;
tempTag1 `${ (x: number) => x }${ y => y }${ undefined }`;
function tempTag2(templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number;
function tempTag2(templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string;
function tempTag2(...rest: any[]): any {
return undefined;
}
tempTag2 `${ x => x }${ 0 }`;
tempTag2 `${ x => x }${ y => y }${ "hello" }`;
tempTag2 `${ x => x }${ 0 }`;
//// [taggedTemplateContextualTyping.js]
function tempTag1() {
var rest = [];
for (var _i = 0; _i < arguments.length; _i++) {
rest[_i - 0] = arguments[_i];
}
return undefined;
}
tempTag1 `${function (x) { return x; }}${10}`;
tempTag1 `${function (x) { return x; }}${function (y) { return y; }}${10}`;
tempTag1 `${function (x) { return x; }}${function (y) { return y; }}${undefined}`;
tempTag1 `${function (x) { return x; }}${function (y) { return y; }}${undefined}`;
function tempTag2() {
var rest = [];
for (var _i = 0; _i < arguments.length; _i++) {
rest[_i - 0] = arguments[_i];
}
return undefined;
}
tempTag2 `${function (x) { return x; }}${0}`;
tempTag2 `${function (x) { return x; }}${function (y) { return y; }}${"hello"}`;
tempTag2 `${function (x) { return x; }}${0}`;

View File

@ -0,0 +1,124 @@
=== tests/cases/conformance/expressions/contextualTyping/taggedTemplateContextualTyping.ts ===
function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T;
>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; }
>T : T
>templateStrs : TemplateStringsArray
>TemplateStringsArray : TemplateStringsArray
>f : (x: T) => T
>x : T
>T : T
>T : T
>x : T
>T : T
>T : T
function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T;
>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; }
>T : T
>templateStrs : TemplateStringsArray
>TemplateStringsArray : TemplateStringsArray
>f : (x: T) => T
>x : T
>T : T
>T : T
>h : (y: T) => T
>y : T
>T : T
>T : T
>x : T
>T : T
>T : T
function tempTag1<T>(...rest: any[]): T {
>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; }
>T : T
>rest : any[]
>T : T
return undefined;
>undefined : undefined
}
tempTag1 `${ x => x }${ 10 }`;
>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; }
>x => x : (x: number) => number
>x : number
>x : number
tempTag1 `${ x => x }${ y => y }${ 10 }`;
>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; }
>x => x : (x: number) => number
>x : number
>x : number
>y => y : (y: number) => number
>y : number
>y : number
tempTag1 `${ x => x }${ (y: number) => y }${ undefined }`;
>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; }
>x => x : (x: number) => number
>x : number
>x : number
>(y: number) => y : (y: number) => number
>y : number
>y : number
>undefined : undefined
tempTag1 `${ (x: number) => x }${ y => y }${ undefined }`;
>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; }
>(x: number) => x : (x: number) => number
>x : number
>x : number
>y => y : (y: number) => number
>y : number
>y : number
>undefined : undefined
function tempTag2(templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number;
>tempTag2 : { (templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number; (templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string; }
>templateStrs : TemplateStringsArray
>TemplateStringsArray : TemplateStringsArray
>f : (x: number) => number
>x : number
>x : number
function tempTag2(templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string;
>tempTag2 : { (templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number; (templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string; }
>templateStrs : TemplateStringsArray
>TemplateStringsArray : TemplateStringsArray
>f : (x: string) => string
>x : string
>h : (y: string) => string
>y : string
>x : string
function tempTag2(...rest: any[]): any {
>tempTag2 : { (templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number; (templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string; }
>rest : any[]
return undefined;
>undefined : undefined
}
tempTag2 `${ x => x }${ 0 }`;
>tempTag2 : { (templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number; (templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string; }
>x => x : (x: number) => number
>x : number
>x : number
tempTag2 `${ x => x }${ y => y }${ "hello" }`;
>tempTag2 : { (templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number; (templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string; }
>x => x : (x: string) => string
>x : string
>x : string
>y => y : (y: string) => string
>y : string
>y : string
tempTag2 `${ x => x }${ 0 }`;
>tempTag2 : { (templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number; (templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string; }
>x => x : (x: number) => number
>x : number
>x : number

View File

@ -0,0 +1,22 @@
// @target: ES6
function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T;
function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T;
function tempTag1<T>(...rest: any[]): T {
return undefined;
}
tempTag1 `${ x => x }${ 10 }`;
tempTag1 `${ x => x }${ y => y }${ 10 }`;
tempTag1 `${ x => x }${ (y: number) => y }${ undefined }`;
tempTag1 `${ (x: number) => x }${ y => y }${ undefined }`;
function tempTag2(templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number;
function tempTag2(templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string;
function tempTag2(...rest: any[]): any {
return undefined;
}
tempTag2 `${ x => x }${ 0 }`;
tempTag2 `${ x => x }${ y => y }${ "hello" }`;
tempTag2 `${ x => x }${ 0 }`;

View File

@ -0,0 +1,44 @@
/// <reference path="fourslash.ts" />
////function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, x: T): T;
////function tempTag1<T>(templateStrs: TemplateStringsArray, f: (x: T) => T, h: (y: T) => T, x: T): T;
////function tempTag1<T>(...rest: any[]): T {
//// return undefined;
////}
////
////tempTag1 `${ x => /*0*/x }${ 10 }`;
////tempTag1 `${ x => /*1*/x }${ x => /*2*/x }${ 10 }`;
////tempTag1 `${ x => /*3*/x }${ (x: number) => /*4*/x }${ undefined }`;
////tempTag1 `${ (x: number) => /*5*/x }${ x => /*6*/x }${ undefined }`;
////
////function tempTag2(templateStrs: TemplateStringsArray, f: (x: number) => number, x: number): number;
////function tempTag2(templateStrs: TemplateStringsArray, f: (x: string) => string, h: (y: string) => string, x: string): string;
////function tempTag2(...rest: any[]): any {
//// return undefined;
////}
////
////tempTag2 `${ x => /*7*/x }${ 0 }`;
////tempTag2 `${ x => /*8*/x }${ undefined }`;
////tempTag2 `${ x => /*9*/x }${ x => /*10*/x }${ "hello" }`;
////tempTag2 `${ x => /*11*/x }${ undefined }${ "hello" }`;
// The first group of parameters, [0, 8], should all be contextually typed as 'number'.
// The second group, [9, 11], should be typed as 'string'.
var numTypedVariableCount = 9;
var strTypedVariableCount = 3;
var markers = test.markers();
if (numTypedVariableCount + strTypedVariableCount !== markers.length) {
throw "Unexpected number of markers in file.";
}
for (var i = 0; i < numTypedVariableCount; i++) {
goTo.marker("" + i);
verify.quickInfoIs("(parameter) x: number");
}
for (var i = 0; i < strTypedVariableCount; i++) {
goTo.marker("" + (i + numTypedVariableCount));
verify.quickInfoIs("(parameter) x: string");
}