Skip to content

Commit 4aafe1d

Browse files
Addressed CR feedback.
1 parent 7fad769 commit 4aafe1d

File tree

3 files changed

+38
-17
lines changed

3 files changed

+38
-17
lines changed

src/compiler/checker.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -5945,10 +5945,17 @@ module ts {
59455945
return getUnionType([type1, type2]);
59465946
}
59475947

5948-
function checkTemplateExpression(node: TemplateExpression): void {
5948+
function checkTemplateExpression(node: TemplateExpression): Type {
5949+
// We just want to check each expressions, but we are unconcerned with
5950+
// the type of each expression, as any value may be coerced into a string.
5951+
// It is worth asking whether this is what we really want though.
5952+
// A place where we actually *are* concerned with the expressions' types are
5953+
// in tagged templates.
59495954
forEach((<TemplateExpression>node).templateSpans, templateSpan => {
59505955
checkExpression(templateSpan.expression);
59515956
});
5957+
5958+
return stringType;
59525959
}
59535960

59545961
function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper?: TypeMapper): Type {
@@ -6005,8 +6012,7 @@ module ts {
60056012
case SyntaxKind.NumericLiteral:
60066013
return numberType;
60076014
case SyntaxKind.TemplateExpression:
6008-
checkTemplateExpression(<TemplateExpression>node);
6009-
// fall through
6015+
return checkTemplateExpression(<TemplateExpression>node);
60106016
case SyntaxKind.StringLiteral:
60116017
case SyntaxKind.NoSubstitutionTemplateLiteral:
60126018
return stringType;

src/compiler/emitter.ts

+16-3
Original file line numberDiff line numberDiff line change
@@ -806,12 +806,14 @@ module ts {
806806
}
807807

808808
function getTemplateLiteralAsStringLiteral(node: LiteralExpression): string {
809-
return "\"" + escapeString(node.text) + "\"";
809+
return '"' + escapeString(node.text) + '"';
810810
}
811811

812812
function emitTemplateExpression(node: TemplateExpression): void {
813+
// In ES6 mode and above, we can simply emit each portion of a template in order, but in
814+
// ES3 & ES5 we must convert the template expression into a series of string concatenations.
813815
if (compilerOptions.target >= ScriptTarget.ES6) {
814-
forEachChild(node, emitNode);
816+
forEachChild(node, emit);
815817
return;
816818
}
817819

@@ -825,6 +827,15 @@ module ts {
825827
emitLiteral(node.head);
826828

827829
forEach(node.templateSpans, templateSpan => {
830+
// Check if the expression has operands and binds its operands less closely than binary '+'.
831+
// If it does, we need to wrap the expression in parentheses. Otherwise, something like
832+
// `abc${ 1 << 2}`
833+
// becomes
834+
// "abc" + 1 << 2 + ""
835+
// which is really
836+
// ("abc" + 1) << (2 + "")
837+
// rather than
838+
// "abc" + (1 << 2) + ""
828839
var needsParens = comparePrecedenceToBinaryPlus(templateSpan.expression) !== Comparison.GreaterThan;
829840

830841
write(" + ");
@@ -858,6 +869,7 @@ module ts {
858869
//
859870
// TODO (drosen): Note that we need to account for the upcoming 'yield' and
860871
// spread ('...') unary operators that are anticipated for ES6.
872+
Debug.assert(compilerOptions.target <= ScriptTarget.ES5);
861873
switch (expression.kind) {
862874
case SyntaxKind.BinaryExpression:
863875
switch ((<BinaryExpression>expression).operator) {
@@ -880,7 +892,8 @@ module ts {
880892
}
881893

882894
function emitTemplateSpan(span: TemplateSpan) {
883-
forEachChild(span, emitNode);
895+
emit(span.expression);
896+
emit(span.literal);
884897
}
885898

886899
// This function specifically handles numeric/string literals for enum and accessor 'identifiers'.

src/compiler/scanner.ts

+13-11
Original file line numberDiff line numberDiff line change
@@ -519,10 +519,10 @@ module ts {
519519
return +(text.substring(start, pos));
520520
}
521521

522-
function scanHexDigits(count: number, useExactCount?: boolean): number {
522+
function scanHexDigits(count: number, mustMatchCount?: boolean): number {
523523
var digits = 0;
524524
var value = 0;
525-
while (digits < count || !useExactCount) {
525+
while (digits < count || !mustMatchCount) {
526526
var ch = text.charCodeAt(pos);
527527
if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) {
528528
value = value * 16 + ch - CharacterCodes._0;
@@ -582,18 +582,18 @@ module ts {
582582
* a literal component of a TemplateExpression.
583583
*/
584584
function scanTemplateAndSetTokenValue(): SyntaxKind {
585-
var isStartOfTemplate = text.charCodeAt(pos) === CharacterCodes.backtick;
585+
var startedWithBacktick = text.charCodeAt(pos) === CharacterCodes.backtick;
586586

587587
pos++;
588588
var start = pos;
589589
var contents = ""
590-
var resultingToken = SyntaxKind.Unknown;
590+
var resultingToken: SyntaxKind;
591591

592592
while (true) {
593593
if (pos >= len) {
594594
contents += text.substring(start, pos);
595595
error(Diagnostics.Unexpected_end_of_text);
596-
resultingToken = isStartOfTemplate ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail;
596+
resultingToken = startedWithBacktick ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail;
597597
break;
598598
}
599599

@@ -603,15 +603,15 @@ module ts {
603603
if (currChar === CharacterCodes.backtick) {
604604
contents += text.substring(start, pos);
605605
pos++;
606-
resultingToken = isStartOfTemplate ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail;
606+
resultingToken = startedWithBacktick ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail;
607607
break;
608608
}
609609

610610
// '${'
611611
if (currChar === CharacterCodes.$ && pos + 1 < len && text.charCodeAt(pos + 1) === CharacterCodes.openBrace) {
612612
contents += text.substring(start, pos);
613613
pos += 2;
614-
resultingToken = isStartOfTemplate ? SyntaxKind.TemplateHead : SyntaxKind.TemplateMiddle;
614+
resultingToken = startedWithBacktick ? SyntaxKind.TemplateHead : SyntaxKind.TemplateMiddle;
615615
break;
616616
}
617617

@@ -641,6 +641,8 @@ module ts {
641641
pos++;
642642
}
643643

644+
Debug.assert(resultingToken !== undefined);
645+
644646
tokenValue = contents;
645647
return resultingToken;
646648
}
@@ -673,7 +675,7 @@ module ts {
673675
return "\"";
674676
case CharacterCodes.x:
675677
case CharacterCodes.u:
676-
var ch = scanHexDigits(ch === CharacterCodes.x ? 2 : 4, /*useExactCount*/ true);
678+
var ch = scanHexDigits(ch === CharacterCodes.x ? 2 : 4, /*mustMatchCount*/ true);
677679
if (ch >= 0) {
678680
return String.fromCharCode(ch);
679681
}
@@ -704,7 +706,7 @@ module ts {
704706
if (pos + 5 < len && text.charCodeAt(pos + 1) === CharacterCodes.u) {
705707
var start = pos;
706708
pos += 2;
707-
var value = scanHexDigits(4, /*useExactCount*/ true);
709+
var value = scanHexDigits(4, /*mustMatchCount*/ true);
708710
pos = start;
709711
return value;
710712
}
@@ -922,7 +924,7 @@ module ts {
922924
case CharacterCodes._0:
923925
if (pos + 2 < len && (text.charCodeAt(pos + 1) === CharacterCodes.X || text.charCodeAt(pos + 1) === CharacterCodes.x)) {
924926
pos += 2;
925-
var value = scanHexDigits(1, /*useExactCount*/ false);
927+
var value = scanHexDigits(1, /*mustMatchCount*/ false);
926928
if (value < 0) {
927929
error(Diagnostics.Hexadecimal_digit_expected);
928930
value = 0;
@@ -1112,7 +1114,7 @@ module ts {
11121114
* Unconditionally back up and scan a template expression portion.
11131115
*/
11141116
function reScanTemplateToken(): SyntaxKind {
1115-
Debug.assert("'reScanTemplateToken' should only be called on a '}'");
1117+
Debug.assert(token === SyntaxKind.CloseBraceToken, "'reScanTemplateToken' should only be called on a '}'");
11161118
pos = tokenPos;
11171119
return token = scanTemplateAndSetTokenValue();
11181120
}

0 commit comments

Comments
 (0)