Skip to content

Commit

Permalink
Some tweaks for backtick strings
Browse files Browse the repository at this point in the history
1. `getRawLiteral()`: barf if `currentSourceFile` is missing, since if
   it is, then the following `getSourceTextOfNodeFromSourceFile` will
   return a bogus `""`.

2. One `||` -> `??` change.

3. `backtickQuoteEscapedCharsRegExp`: escape the usual control
   characters except for a simple LF.  This code does get used to
   generate backtick strings when `rawText` is not given, and not
   escaping things like TAB characters can get mangled by editor
   settings.  Worse, not escaping a CRLF and putting it verbatim in sthe
   string source will interpret it as LF, so add a special case for
   escaping these as `\r\n`.
   Added test.

Related to microsoft#44313 and microsoft#40625.
  • Loading branch information
elibarzilay committed Aug 5, 2021
1 parent 3266ffb commit 635f5bd
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/compiler/transformers/taggedTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ namespace ts {
// Examples: `\n` is converted to "\\n", a template string with a newline to "\n".
let text = node.rawText;
if (text === undefined) {
Debug.assertIsDefined(currentSourceFile,
"Template literal node is missing 'rawText' and does not have a source file. Possibly bad transform.");
text = getSourceTextOfNodeFromSourceFile(currentSourceFile, node);

// text contains the original source, it will also contain quotes ("`"), dolar signs and braces ("${" and "}"),
Expand Down
9 changes: 5 additions & 4 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ namespace ts {
const escapeText = flags & GetLiteralTextFlags.NeverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? escapeString :
escapeNonAsciiString;

const rawText = (node as TemplateLiteralLikeNode).rawText || escapeTemplateSubstitution(escapeText(node.text, CharacterCodes.backtick));
const rawText = (node as TemplateLiteralLikeNode).rawText ?? escapeTemplateSubstitution(escapeText(node.text, CharacterCodes.backtick));
switch (node.kind) {
case SyntaxKind.NoSubstitutionTemplateLiteral:
return "`" + rawText + "`";
Expand Down Expand Up @@ -3851,8 +3851,8 @@ namespace ts {
// There is no reason for this other than that JSON.stringify does not handle it either.
const doubleQuoteEscapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
const singleQuoteEscapedCharsRegExp = /[\\\'\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
// Template strings should be preserved as much as possible
const backtickQuoteEscapedCharsRegExp = /[\\`]/g;
// Template strings preserve simple LF newlines, still encode CRLF (or CR)
const backtickQuoteEscapedCharsRegExp = /\r\n|[\\\`\u0000-\u001f\t\v\f\b\r\u2028\u2029\u0085]/g;
const escapedCharsMap = new Map(getEntries({
"\t": "\\t",
"\v": "\\v",
Expand All @@ -3866,7 +3866,8 @@ namespace ts {
"\`": "\\\`",
"\u2028": "\\u2028", // lineSeparator
"\u2029": "\\u2029", // paragraphSeparator
"\u0085": "\\u0085" // nextLine
"\u0085": "\\u0085", // nextLine
"\r\n": "\\r\\n", // special case for CRLFs in backticks
}));

function encodeUtf16EscapeSequence(charCode: number): string {
Expand Down
14 changes: 14 additions & 0 deletions tests/baselines/reference/templateLiteralsInTypes.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
tests/cases/compiler/templateLiteralsInTypes.ts(3,1): error TS2554: Expected 2 arguments, but got 1.
tests/cases/compiler/templateLiteralsInTypes.ts(3,8): error TS2339: Property 'foo' does not exist on type '`${string}:\t${number}\r\n`'.


==== tests/cases/compiler/templateLiteralsInTypes.ts (2 errors) ====
const f = (hdr: string, val: number) => `${hdr}:\t${val}\r\n` as `${string}:\t${number}\r\n`;

f("x").foo;
~~~~~~
!!! error TS2554: Expected 2 arguments, but got 1.
!!! related TS6210 tests/cases/compiler/templateLiteralsInTypes.ts:1:25: An argument for 'val' was not provided.
~~~
!!! error TS2339: Property 'foo' does not exist on type '`${string}:\t${number}\r\n`'.

14 changes: 14 additions & 0 deletions tests/baselines/reference/templateLiteralsInTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//// [templateLiteralsInTypes.ts]
const f = (hdr: string, val: number) => `${hdr}:\t${val}\r\n` as `${string}:\t${number}\r\n`;

f("x").foo;


//// [templateLiteralsInTypes.js]
"use strict";
var f = function (hdr, val) { return hdr + ":\t" + val + "\r\n"; };
f("x").foo;


//// [templateLiteralsInTypes.d.ts]
declare const f: (hdr: string, val: number) => `${string}:\t${number}\r\n`;
11 changes: 11 additions & 0 deletions tests/baselines/reference/templateLiteralsInTypes.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
=== tests/cases/compiler/templateLiteralsInTypes.ts ===
const f = (hdr: string, val: number) => `${hdr}:\t${val}\r\n` as `${string}:\t${number}\r\n`;
>f : Symbol(f, Decl(templateLiteralsInTypes.ts, 0, 5))
>hdr : Symbol(hdr, Decl(templateLiteralsInTypes.ts, 0, 11))
>val : Symbol(val, Decl(templateLiteralsInTypes.ts, 0, 23))
>hdr : Symbol(hdr, Decl(templateLiteralsInTypes.ts, 0, 11))
>val : Symbol(val, Decl(templateLiteralsInTypes.ts, 0, 23))

f("x").foo;
>f : Symbol(f, Decl(templateLiteralsInTypes.ts, 0, 5))

18 changes: 18 additions & 0 deletions tests/baselines/reference/templateLiteralsInTypes.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
=== tests/cases/compiler/templateLiteralsInTypes.ts ===
const f = (hdr: string, val: number) => `${hdr}:\t${val}\r\n` as `${string}:\t${number}\r\n`;
>f : (hdr: string, val: number) => `${string}:\t${number}\r\n`
>(hdr: string, val: number) => `${hdr}:\t${val}\r\n` as `${string}:\t${number}\r\n` : (hdr: string, val: number) => `${string}:\t${number}\r\n`
>hdr : string
>val : number
>`${hdr}:\t${val}\r\n` as `${string}:\t${number}\r\n` : `${string}:\t${number}\r\n`
>`${hdr}:\t${val}\r\n` : `${string}:\t${number}\r\n`
>hdr : string
>val : number

f("x").foo;
>f("x").foo : any
>f("x") : `${string}:\t${number}\r\n`
>f : (hdr: string, val: number) => `${string}:\t${number}\r\n`
>"x" : "x"
>foo : any

6 changes: 6 additions & 0 deletions tests/cases/compiler/templateLiteralsInTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @strict: true
// @declaration: true

const f = (hdr: string, val: number) => `${hdr}:\t${val}\r\n` as `${string}:\t${number}\r\n`;

f("x").foo;

0 comments on commit 635f5bd

Please sign in to comment.