Skip to content

Type checking for tagged template expressions #1072

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Nov 11, 2014
296 changes: 224 additions & 72 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@ module ts {
return result;
}

/**
* Returns the last element of an array if non-empty, undefined otherwise.
*/
export function lastOrUndefined<T>(array: T[]): T {
if (array.length === 0) {
return undefined;
}

return array[array.length - 1];
}

export function binarySearch(array: number[], value: number): number {
var low = 0;
var high = array.length - 1;
Expand Down
15 changes: 14 additions & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,6 @@ module ts {
return false;
}


export function isDeclaration(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.TypeParameter:
Expand Down Expand Up @@ -796,6 +795,20 @@ module ts {
return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken;
}

export function isUnterminatedTemplateEnd(node: LiteralExpression) {
Debug.assert(node.kind === SyntaxKind.NoSubstitutionTemplateLiteral || node.kind === SyntaxKind.TemplateTail);
var sourceText = getSourceFileOfNode(node).text;

// If we're not at the EOF, we know we must be terminated.
if (node.end !== sourceText.length) {
return false;
}

// If we didn't end in a backtick, we must still be in the middle of a template.
// If we did, make sure that it's not the *initial* backtick.
return sourceText.charCodeAt(node.end - 1) !== CharacterCodes.backtick || node.text.length === 0;
}

export function isModifier(token: SyntaxKind): boolean {
switch (token) {
case SyntaxKind.PublicKeyword:
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,8 @@ module ts {
template: LiteralExpression | TemplateExpression;
}

export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression;

export interface TypeAssertion extends Expression {
type: TypeNode;
operand: Expression;
Expand Down
4 changes: 4 additions & 0 deletions src/lib/core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,10 @@ declare var Number: {
POSITIVE_INFINITY: number;
}

interface TemplateStringsArray extends Array<string> {
raw: string[];
}

interface Math {
/** The mathematical constant e. This is Euler's number, the base of natural logarithms. */
E: number;
Expand Down
2 changes: 2 additions & 0 deletions tests/baselines/reference/noDefaultLib.errors.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
error TS2318: Cannot find global type 'Boolean'.
error TS2318: Cannot find global type 'IArguments'.
error TS2318: Cannot find global type 'TemplateStringsArray'.
tests/cases/compiler/noDefaultLib.ts(4,11): error TS2317: Global type 'Array' must have 1 type parameter(s).


!!! error TS2318: Cannot find global type 'Boolean'.
!!! error TS2318: Cannot find global type 'IArguments'.
!!! error TS2318: Cannot find global type 'TemplateStringsArray'.
==== tests/cases/compiler/noDefaultLib.ts (1 errors) ====
/// <reference no-default-lib="true"/>
var x;
Expand Down
2 changes: 2 additions & 0 deletions tests/baselines/reference/parser509698.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ error TS2318: Cannot find global type 'Number'.
error TS2318: Cannot find global type 'Object'.
error TS2318: Cannot find global type 'RegExp'.
error TS2318: Cannot find global type 'String'.
error TS2318: Cannot find global type 'TemplateStringsArray'.


!!! error TS2318: Cannot find global type 'Array'.
Expand All @@ -16,6 +17,7 @@ error TS2318: Cannot find global type 'String'.
!!! error TS2318: Cannot find global type 'Object'.
!!! error TS2318: Cannot find global type 'RegExp'.
!!! error TS2318: Cannot find global type 'String'.
!!! error TS2318: Cannot find global type 'TemplateStringsArray'.
==== tests/cases/conformance/parser/ecmascript5/RegressionTests/parser509698.ts (0 errors) ====
/// <style requireSemi="on" />
/// <reference no-default-lib="true"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ error TS2318: Cannot find global type 'Number'.
error TS2318: Cannot find global type 'Object'.
error TS2318: Cannot find global type 'RegExp'.
error TS2318: Cannot find global type 'String'.
error TS2318: Cannot find global type 'TemplateStringsArray'.
test.ts(3,8): error TS2304: Cannot find name 'Array'.


Expand All @@ -17,6 +18,7 @@ test.ts(3,8): error TS2304: Cannot find name 'Array'.
!!! error TS2318: Cannot find global type 'Object'.
!!! error TS2318: Cannot find global type 'RegExp'.
!!! error TS2318: Cannot find global type 'String'.
!!! error TS2318: Cannot find global type 'TemplateStringsArray'.
==== test.ts (1 errors) ====
/// <reference no-default-lib="true"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ error TS2318: Cannot find global type 'Number'.
error TS2318: Cannot find global type 'Object'.
error TS2318: Cannot find global type 'RegExp'.
error TS2318: Cannot find global type 'String'.
error TS2318: Cannot find global type 'TemplateStringsArray'.
test.ts(3,8): error TS2304: Cannot find name 'Array'.


Expand All @@ -17,6 +18,7 @@ test.ts(3,8): error TS2304: Cannot find name 'Array'.
!!! error TS2318: Cannot find global type 'Object'.
!!! error TS2318: Cannot find global type 'RegExp'.
!!! error TS2318: Cannot find global type 'String'.
!!! error TS2318: Cannot find global type 'TemplateStringsArray'.
==== test.ts (1 errors) ====
/// <reference no-default-lib="true"/>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(5,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(9,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(13,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(16,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(20,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(23,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(27,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(28,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(29,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(33,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(34,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(35,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(39,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(40,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(41,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(45,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(46,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(47,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(51,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(52,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(53,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(57,9): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(58,1): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(64,11): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(77,11): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(81,11): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(86,9): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(90,11): error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(64,11): error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly.
Type argument candidate 'string' is not a valid type argument because it is not a supertype of candidate 'number'.
tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts(77,11): error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly.
Type argument candidate '{ x: number; z: Date; }' is not a valid type argument because it is not a supertype of candidate '{ x: number; y: string; }'.


==== tests/cases/conformance/es6/templates/taggedTemplateStringsTypeArgumentInference.ts (30 errors) ====


// Generic tag with one parameter
function noParams<T>(n: T) { }
noParams ``;
~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.

// Generic tag with parameter which does not use type parameter
function noGenericParams<T>(n: string[]) { }
noGenericParams ``;
~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.

// Generic tag with multiple type parameters and only one used in parameter type annotation
function someGenerics1a<T, U>(n: T, m: number) { }
someGenerics1a `${3}`;
~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.

function someGenerics1b<T, U>(n: string[], m: U) { }
someGenerics1b `${3}`;
~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.

// Generic tag with argument of function type whose parameter is of type parameter type
function someGenerics2a<T>(strs: string[], n: (x: T) => void) { }
someGenerics2a `${(n: string) => n}`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.

function someGenerics2b<T, U>(strs: string[], n: (x: T, y: U) => void) { }
someGenerics2b `${ (n: string, x: number) => n }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.

// Generic tag with argument of function type whose parameter is not of type parameter type but body/return type uses type parameter
function someGenerics3<T>(strs: string[], producer: () => T) { }
someGenerics3 `${() => ''}`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
someGenerics3 `${() => undefined}`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
someGenerics3 `${() => 3}`;
~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.

// 2 parameter generic tag with argument 1 of type parameter type and argument 2 of function type whose parameter is of type parameter type
function someGenerics4<T, U>(strs: string[], n: T, f: (x: U) => void) { }
someGenerics4 `${4}${ () => null }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
someGenerics4 `${''}${ () => 3 }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
someGenerics4 `${ null }${ null }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.

// 2 parameter generic tag with argument 2 of type parameter type and argument 1 of function type whose parameter is of type parameter type
function someGenerics5<U, T>(strs: string[], n: T, f: (x: U) => void) { }
someGenerics5 `${ 4 } ${ () => null }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
someGenerics5 `${ '' }${ () => 3 }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
someGenerics5 `${null}${null}`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.

// Generic tag with multiple arguments of function types that each have parameters of the same generic type
function someGenerics6<A>(strs: string[], a: (a: A) => A, b: (b: A) => A, c: (c: A) => A) { }
someGenerics6 `${ n => n }${ n => n}${ n => n}`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
someGenerics6 `${ n => n }${ n => n}${ n => n}`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
someGenerics6 `${ (n: number) => n }${ (n: number) => n }${ (n: number) => n }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.

// Generic tag with multiple arguments of function types that each have parameters of different generic type
function someGenerics7<A, B, C>(strs: string[], a: (a: A) => A, b: (b: B) => B, c: (c: C) => C) { }
someGenerics7 `${ n => n }${ n => n }${ n => n }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
someGenerics7 `${ n => n }${ n => n }${ n => n }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
someGenerics7 `${(n: number) => n}${ (n: string) => n}${ (n: number) => n}`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.

// Generic tag with argument of generic function type
function someGenerics8<T>(strs: string[], n: T): T { return n; }
var x = someGenerics8 `${ someGenerics7 }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
x `${null}${null}${null}`;
~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.

// Generic tag with multiple parameters of generic type passed arguments with no best common type
function someGenerics9<T>(strs: string[], a: T, b: T, c: T): T {
return null;
}
var a9a = someGenerics9 `${ '' }${ 0 }${ [] }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
~~~~~~~~~~~~~
!!! error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly.
!!! error TS2453: Type argument candidate 'string' is not a valid type argument because it is not a supertype of candidate 'number'.
var a9a: {};

// Generic tag with multiple parameters of generic type passed arguments with multiple best common types
interface A91 {
x: number;
y?: string;
}
interface A92 {
x: number;
z?: Date;
}

var a9e = someGenerics9 `${ undefined }${ { x: 6, z: new Date() } }${ { x: 6, y: '' } }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
~~~~~~~~~~~~~
!!! error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly.
!!! error TS2453: Type argument candidate '{ x: number; z: Date; }' is not a valid type argument because it is not a supertype of candidate '{ x: number; y: string; }'.
var a9e: {};

// Generic tag with multiple parameters of generic type passed arguments with a single best common type
var a9d = someGenerics9 `${ { x: 3 }}${ { x: 6 }}${ { x: 6 } }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
var a9d: { x: number; };

// Generic tag with multiple parameters of generic type where one argument is of type 'any'
var anyVar: any;
var a = someGenerics9 `${ 7 }${ anyVar }${ 4 }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
var a: any;

// Generic tag with multiple parameters of generic type where one argument is [] and the other is not 'any'
var arr = someGenerics9 `${ [] }${ null }${ undefined }`;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1159: Tagged templates are only available when targeting ECMAScript 6 and higher.
var arr: any[];


Loading