From da7967a3cf535c131791e94f52bc0eb6d872fae3 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Fri, 13 Apr 2018 23:15:02 -0700 Subject: [PATCH] Added basic support for parsing/emitting type arguments in tagged template expressions. --- src/compiler/emitter.ts | 1 + src/compiler/parser.ts | 36 +++++++++++++++++++++++++++--------- src/compiler/types.ts | 1 + 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 289f332cd33..2b01f2fdda1 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1462,6 +1462,7 @@ namespace ts { function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { emitExpression(node.tag); + emitTypeArguments(node, node.typeArguments); writeSpace(); emitExpression(node.template); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 74d54ed28fb..29d57c846f5 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -223,6 +223,7 @@ namespace ts { visitNodes(cbNode, cbNodes, (node).arguments); case SyntaxKind.TaggedTemplateExpression: return visitNode(cbNode, (node).tag) || + visitNodes(cbNode, cbNodes, (node).typeArguments) || visitNode(cbNode, (node).template); case SyntaxKind.TypeAssertionExpression: return visitNode(cbNode, (node).type) || @@ -4362,18 +4363,28 @@ namespace ts { continue; } - if (token() === SyntaxKind.NoSubstitutionTemplateLiteral || token() === SyntaxKind.TemplateHead) { - const tagExpression = createNode(SyntaxKind.TaggedTemplateExpression, expression.pos); - tagExpression.tag = expression; - tagExpression.template = token() === SyntaxKind.NoSubstitutionTemplateLiteral - ? parseLiteralNode() - : parseTemplateExpression(); - expression = finishNode(tagExpression); + if (isTemplateStartOfTaggedTemplate()) { + expression = parseTaggedTemplateRest(expression, /*typeArguments*/ undefined); continue; } return expression; } + + } + + function isTemplateStartOfTaggedTemplate() { + return token() === SyntaxKind.NoSubstitutionTemplateLiteral || token() === SyntaxKind.TemplateHead; + } + + function parseTaggedTemplateRest(tag: LeftHandSideExpression, typeArguments: NodeArray | undefined) { + const tagExpression = createNode(SyntaxKind.TaggedTemplateExpression, tag.pos); + tagExpression.tag = tag; + tagExpression.typeArguments = typeArguments; + tagExpression.template = token() === SyntaxKind.NoSubstitutionTemplateLiteral + ? parseLiteralNode() + : parseTemplateExpression(); + return finishNode(tagExpression); } function parseCallExpressionRest(expression: LeftHandSideExpression): LeftHandSideExpression { @@ -4389,6 +4400,11 @@ namespace ts { return expression; } + if (isTemplateStartOfTaggedTemplate()) { + expression = parseTaggedTemplateRest(expression, typeArguments); + continue; + } + const callExpr = createNode(SyntaxKind.CallExpression, expression.pos); callExpr.expression = expression; callExpr.typeArguments = typeArguments; @@ -4436,8 +4452,10 @@ namespace ts { function canFollowTypeArgumentsInExpression(): boolean { switch (token()) { case SyntaxKind.OpenParenToken: // foo( - // this case are the only case where this token can legally follow a type argument - // list. So we definitely want to treat this as a type arg list. + case SyntaxKind.NoSubstitutionTemplateLiteral: // foo `...` + case SyntaxKind.TemplateHead: // foo `...${100}...` + // these are the only tokens can legally follow a type argument + // list. So we definitely want to treat them as type arg lists. case SyntaxKind.DotToken: // foo. case SyntaxKind.CloseParenToken: // foo) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 987d403e7cd..f96ea05cb12 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1726,6 +1726,7 @@ namespace ts { export interface TaggedTemplateExpression extends MemberExpression { kind: SyntaxKind.TaggedTemplateExpression; tag: LeftHandSideExpression; + typeArguments?: NodeArray; template: TemplateLiteral; }