[ts] Add support for template interpolations in types (#12131)

Co-authored-by: Brian Ng <bng412@gmail.com>
This commit is contained in:
Nicolò Ribaudo 2020-10-14 20:15:42 +02:00 committed by GitHub
parent 9f40d6fcd0
commit 3fd963fdc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 106 additions and 19 deletions

View File

@ -1,6 +1,6 @@
FLOW_COMMIT = a1f9a4c709dcebb27a5084acf47755fbae699c25
TEST262_COMMIT = 058adfed86b1d4129996faaf50a85ea55379a66a
TYPESCRIPT_COMMIT = d779a190535e52896cfe5100101173c00b6b8625
TYPESCRIPT_COMMIT = da8633212023517630de5f3620a23736b63234b1
FORCE_PUBLISH = "@babel/runtime,@babel/runtime-corejs2,@babel/runtime-corejs3,@babel/standalone"

View File

@ -1608,7 +1608,7 @@ export default class ExpressionParser extends LValParser {
node.quasis = [curElt];
while (!curElt.tail) {
this.expect(tt.dollarBraceL);
node.expressions.push(this.parseExpression());
node.expressions.push(this.parseTemplateSubstitution());
this.expect(tt.braceR);
node.quasis.push((curElt = this.parseTemplateElement(isTagged)));
}
@ -1616,6 +1616,11 @@ export default class ExpressionParser extends LValParser {
return this.finishNode(node, "TemplateLiteral");
}
// This is overwritten by the TypeScript plugin to parse template types
parseTemplateSubstitution(): N.Expression {
return this.parseExpression();
}
// Parse an object literal, binding pattern, or record.
parseObjectLike<T: N.ObjectPattern | N.ObjectExpression>(

View File

@ -94,8 +94,6 @@ const TSErrors = Object.freeze({
"Private elements cannot have the 'abstract' modifier.",
PrivateElementHasAccessibility:
"Private elements cannot have an accessibility modifier ('%0')",
TemplateTypeHasSubstitution:
"Template literal types cannot have any substitution",
TypeAnnotationAfterAssign:
"Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`",
UnexpectedParameterModifier:
@ -774,17 +772,15 @@ export default (superClass: Class<Parser>): Class<Parser> =>
tsParseTemplateLiteralType(): N.TsType {
const node: N.TsLiteralType = this.startNode();
const templateNode = this.parseTemplate(false);
if (templateNode.expressions.length > 0) {
this.raise(
templateNode.expressions[0].start,
TSErrors.TemplateTypeHasSubstitution,
);
}
node.literal = templateNode;
node.literal = this.parseTemplate(false);
return this.finishNode(node, "TSLiteralType");
}
parseTemplateSubstitution(): N.TsType {
if (this.state.inType) return this.tsParseType();
return super.parseTemplateSubstitution();
}
tsParseThisTypeOrThisTypePredicate(): N.TsThisType | N.TsTypePredicate {
const thisKeyword = this.tsParseThisTypeNode();
if (this.isContextual("is") && !this.hasPrecedingLineBreak()) {

View File

@ -1,9 +1,6 @@
{
"type": "File",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
"errors": [
"SyntaxError: Template literal types cannot have any substitution (1:14)"
],
"program": {
"type": "Program",
"start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}},
@ -32,10 +29,14 @@
"start":7,"end":19,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":19}},
"expressions": [
{
"type": "TSTypeReference",
"start":14,"end":17,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":17}},
"typeName": {
"type": "Identifier",
"start":14,"end":17,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":17},"identifierName":"bar"},
"name": "bar"
}
}
],
"quasis": [
{

View File

@ -0,0 +1 @@
let x: `foo-${bar + baz}`;

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \"}\" (1:18)"
}

View File

@ -0,0 +1 @@
let x: `foo-${infer bar}`;

View File

@ -0,0 +1,73 @@
{
"type": "File",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"program": {
"type": "Program",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "VariableDeclaration",
"start":0,"end":26,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":26}},
"declarations": [
{
"type": "VariableDeclarator",
"start":4,"end":25,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":25}},
"id": {
"type": "Identifier",
"start":4,"end":25,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":25},"identifierName":"x"},
"name": "x",
"typeAnnotation": {
"type": "TSTypeAnnotation",
"start":5,"end":25,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":25}},
"typeAnnotation": {
"type": "TSLiteralType",
"start":7,"end":25,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":25}},
"literal": {
"type": "TemplateLiteral",
"start":7,"end":25,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":25}},
"expressions": [
{
"type": "TSInferType",
"start":14,"end":23,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":23}},
"typeParameter": {
"type": "TSTypeParameter",
"start":20,"end":23,"loc":{"start":{"line":1,"column":20},"end":{"line":1,"column":23}},
"name": "bar"
}
}
],
"quasis": [
{
"type": "TemplateElement",
"start":8,"end":12,"loc":{"start":{"line":1,"column":8},"end":{"line":1,"column":12}},
"value": {
"raw": "foo-",
"cooked": "foo-"
},
"tail": false
},
{
"type": "TemplateElement",
"start":24,"end":24,"loc":{"start":{"line":1,"column":24},"end":{"line":1,"column":24}},
"value": {
"raw": "",
"cooked": ""
},
"tail": true
}
]
}
}
}
},
"init": null
}
],
"kind": "let"
}
],
"directives": []
}
}

View File

@ -1841,7 +1841,13 @@ defineType("TemplateLiteral", {
expressions: {
validate: chain(
assertValueType("array"),
assertEach(assertNodeType("Expression")),
assertEach(
assertNodeType(
"Expression",
// For TypeScript template literal types
"TSType",
),
),
function (node, key, val) {
if (node.quasis.length !== val.length + 1) {
throw new TypeError(

View File

@ -226,6 +226,7 @@ gettersAndSettersErrors.ts
giant.ts
globalThisDeclarationEmit.ts
globalThisDeclarationEmit2.ts
hugeDeclarationOutputGetsTruncatedWithError.ts
implementClausePrecedingExtends.ts
implementsClauseAlreadySeen.ts
importAndVariableDeclarationConflict1.ts