TypeScript: Support type arguments on tagged templates (#7754)
| Q | A | ------------------------ | --- | Fixed Issues? | #7747 (partly) | Patch: Bug Fix? | | Major: Breaking Change? | | Minor: New Feature? | Yes | Tests Added + Pass? | Yes | Documentation PR | | Any Dependency Changes? | | License | MIT @JamesHenry This changes the AST format. CC @DanielRosenwasser for review. Supports parsing type arguments on tagged template calls. Should wait on Microsoft/TypeScript#23430 to be merged so we're sure we have the final syntax.
This commit is contained in:
parent
db2a9fc96e
commit
8ee24fdfc0
@ -1,5 +1,6 @@
|
||||
export function TaggedTemplateExpression(node: Object) {
|
||||
this.print(node.tag, node);
|
||||
this.print(node.typeParameters, node); // TS
|
||||
this.print(node.quasi, node);
|
||||
}
|
||||
|
||||
|
||||
1
packages/babel-generator/test/fixtures/typescript/type-arguments-tagged-template/input.js
vendored
Normal file
1
packages/babel-generator/test/fixtures/typescript/type-arguments-tagged-template/input.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
f<T>``;
|
||||
1
packages/babel-generator/test/fixtures/typescript/type-arguments-tagged-template/output.js
vendored
Normal file
1
packages/babel-generator/test/fixtures/typescript/type-arguments-tagged-template/output.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
f<T>``;
|
||||
@ -563,9 +563,32 @@ export default class ExpressionParser extends LValParser {
|
||||
}
|
||||
return node;
|
||||
} else if (this.match(tt.backQuote)) {
|
||||
const node = this.startNodeAt(startPos, startLoc);
|
||||
return this.parseTaggedTemplateExpression(
|
||||
startPos,
|
||||
startLoc,
|
||||
base,
|
||||
state,
|
||||
);
|
||||
} else {
|
||||
state.stop = true;
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
parseTaggedTemplateExpression(
|
||||
startPos: number,
|
||||
startLoc: Position,
|
||||
base: N.Expression,
|
||||
state: N.ParseSubscriptState,
|
||||
typeArguments?: ?N.TsTypeParameterInstantiation,
|
||||
): N.TaggedTemplateExpression {
|
||||
const node: N.TaggedTemplateExpression = this.startNodeAt(
|
||||
startPos,
|
||||
startLoc,
|
||||
);
|
||||
node.tag = base;
|
||||
node.quasi = this.parseTemplate(true);
|
||||
if (typeArguments) node.typeParameters = typeArguments;
|
||||
if (state.optionalChainMember) {
|
||||
this.raise(
|
||||
startPos,
|
||||
@ -573,10 +596,6 @@ export default class ExpressionParser extends LValParser {
|
||||
);
|
||||
}
|
||||
return this.finishNode(node, "TaggedTemplateExpression");
|
||||
} else {
|
||||
state.stop = true;
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
atPossibleAsync(base: N.Expression): boolean {
|
||||
|
||||
@ -836,16 +836,6 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
return this.finishNode(node, "TSTypeAssertion");
|
||||
}
|
||||
|
||||
tsTryParseTypeArgumentsInExpression(
|
||||
eatNextToken: boolean,
|
||||
): ?N.TsTypeParameterInstantiation {
|
||||
return this.tsTryParseAndCatch(() => {
|
||||
const res = this.tsParseTypeArguments();
|
||||
if (eatNextToken) this.expect(tt.parenL);
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
tsParseHeritageClause(): $ReadOnlyArray<N.TsExpressionWithTypeArguments> {
|
||||
return this.tsParseDelimitedList(
|
||||
"HeritageClauseElement",
|
||||
@ -1376,8 +1366,11 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
return this.finishNode(nonNullExpression, "TSNonNullExpression");
|
||||
}
|
||||
|
||||
if (!noCalls && this.isRelational("<")) {
|
||||
if (this.atPossibleAsync(base)) {
|
||||
// There are number of things we are going to "maybe" parse, like type arguments on
|
||||
// tagged template expressions. If any of them fail, walk it back and continue.
|
||||
const result = this.tsTryParseAndCatch(() => {
|
||||
if (this.isRelational("<")) {
|
||||
if (!noCalls && this.atPossibleAsync(base)) {
|
||||
// Almost certainly this is a generic async function `async <T>() => ...
|
||||
// But it might be a call with a type argument `async<T>();`
|
||||
const asyncArrowFn = this.tsTryParseGenericAsyncArrowFunction(
|
||||
@ -1392,12 +1385,10 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
const node: N.CallExpression = this.startNodeAt(startPos, startLoc);
|
||||
node.callee = base;
|
||||
|
||||
// May be passing type arguments. But may just be the `<` operator.
|
||||
// Note: With `/*eatNextToken*/ true` this also eats the `(` following the type arguments
|
||||
const typeArguments = this.tsTryParseTypeArgumentsInExpression(
|
||||
/*eatNextToken*/ true,
|
||||
);
|
||||
const typeArguments = this.tsParseTypeArguments();
|
||||
|
||||
if (typeArguments) {
|
||||
if (!noCalls && this.eat(tt.parenL)) {
|
||||
// possibleAsync always false here, because we would have handled it above.
|
||||
// $FlowIgnore (won't be any undefined arguments)
|
||||
node.arguments = this.parseCallExpressionArguments(
|
||||
@ -1406,8 +1397,22 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
);
|
||||
node.typeParameters = typeArguments;
|
||||
return this.finishCallExpression(node);
|
||||
} else if (this.match(tt.backQuote)) {
|
||||
return this.parseTaggedTemplateExpression(
|
||||
startPos,
|
||||
startLoc,
|
||||
base,
|
||||
state,
|
||||
typeArguments,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.unexpected();
|
||||
});
|
||||
|
||||
if (result) return result;
|
||||
|
||||
return super.parseSubscript(base, startPos, startLoc, noCalls, state);
|
||||
}
|
||||
@ -2127,8 +2132,8 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
jsxParseOpeningElementAfterName(
|
||||
node: N.JSXOpeningElement,
|
||||
): N.JSXOpeningElement {
|
||||
const typeArguments = this.tsTryParseTypeArgumentsInExpression(
|
||||
/*eatNextToken*/ false,
|
||||
const typeArguments = this.tsTryParseAndCatch(() =>
|
||||
this.tsParseTypeArguments(),
|
||||
);
|
||||
if (typeArguments) node.typeParameters = typeArguments;
|
||||
return super.jsxParseOpeningElementAfterName(node);
|
||||
|
||||
@ -577,6 +577,7 @@ export type TaggedTemplateExpression = NodeBase & {
|
||||
type: "TaggedTemplateExpression",
|
||||
tag: Expression,
|
||||
quasi: TemplateLiteral,
|
||||
typeParameters?: ?TypeParameterInstantiationBase, // TODO: Not in spec
|
||||
};
|
||||
|
||||
export type TemplateElement = NodeBase & {
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
new C<T>
|
||||
``
|
||||
185
packages/babel-parser/test/fixtures/typescript/type-arguments/tagged-template-no-asi/output.json
vendored
Normal file
185
packages/babel-parser/test/fixtures/typescript/type-arguments/tagged-template-no-asi/output.json
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
{
|
||||
"type": "File",
|
||||
"start": 0,
|
||||
"end": 11,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 2
|
||||
}
|
||||
},
|
||||
"program": {
|
||||
"type": "Program",
|
||||
"start": 0,
|
||||
"end": 11,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 2
|
||||
}
|
||||
},
|
||||
"sourceType": "module",
|
||||
"interpreter": null,
|
||||
"body": [
|
||||
{
|
||||
"type": "ExpressionStatement",
|
||||
"start": 0,
|
||||
"end": 11,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 2
|
||||
}
|
||||
},
|
||||
"expression": {
|
||||
"type": "NewExpression",
|
||||
"start": 0,
|
||||
"end": 11,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 2
|
||||
}
|
||||
},
|
||||
"callee": {
|
||||
"type": "TaggedTemplateExpression",
|
||||
"start": 4,
|
||||
"end": 11,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 4
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 2
|
||||
}
|
||||
},
|
||||
"tag": {
|
||||
"type": "Identifier",
|
||||
"start": 4,
|
||||
"end": 5,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 4
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 5
|
||||
},
|
||||
"identifierName": "C"
|
||||
},
|
||||
"name": "C"
|
||||
},
|
||||
"quasi": {
|
||||
"type": "TemplateLiteral",
|
||||
"start": 9,
|
||||
"end": 11,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 2
|
||||
}
|
||||
},
|
||||
"expressions": [],
|
||||
"quasis": [
|
||||
{
|
||||
"type": "TemplateElement",
|
||||
"start": 10,
|
||||
"end": 10,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 1
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 1
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"raw": "",
|
||||
"cooked": ""
|
||||
},
|
||||
"tail": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"typeParameters": {
|
||||
"type": "TSTypeParameterInstantiation",
|
||||
"start": 5,
|
||||
"end": 8,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 8
|
||||
}
|
||||
},
|
||||
"params": [
|
||||
{
|
||||
"type": "TSTypeReference",
|
||||
"start": 6,
|
||||
"end": 7,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 6
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 7
|
||||
}
|
||||
},
|
||||
"typeName": {
|
||||
"type": "Identifier",
|
||||
"start": 6,
|
||||
"end": 7,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 6
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 7
|
||||
},
|
||||
"identifierName": "T"
|
||||
},
|
||||
"name": "T"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"arguments": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"directives": []
|
||||
}
|
||||
}
|
||||
1
packages/babel-parser/test/fixtures/typescript/type-arguments/tagged-template/input.js
vendored
Normal file
1
packages/babel-parser/test/fixtures/typescript/type-arguments/tagged-template/input.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
f<T>``;
|
||||
169
packages/babel-parser/test/fixtures/typescript/type-arguments/tagged-template/output.json
vendored
Normal file
169
packages/babel-parser/test/fixtures/typescript/type-arguments/tagged-template/output.json
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
{
|
||||
"type": "File",
|
||||
"start": 0,
|
||||
"end": 7,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 7
|
||||
}
|
||||
},
|
||||
"program": {
|
||||
"type": "Program",
|
||||
"start": 0,
|
||||
"end": 7,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 7
|
||||
}
|
||||
},
|
||||
"sourceType": "module",
|
||||
"interpreter": null,
|
||||
"body": [
|
||||
{
|
||||
"type": "ExpressionStatement",
|
||||
"start": 0,
|
||||
"end": 7,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 7
|
||||
}
|
||||
},
|
||||
"expression": {
|
||||
"type": "TaggedTemplateExpression",
|
||||
"start": 0,
|
||||
"end": 6,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 6
|
||||
}
|
||||
},
|
||||
"tag": {
|
||||
"type": "Identifier",
|
||||
"start": 0,
|
||||
"end": 1,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 1
|
||||
},
|
||||
"identifierName": "f"
|
||||
},
|
||||
"name": "f"
|
||||
},
|
||||
"quasi": {
|
||||
"type": "TemplateLiteral",
|
||||
"start": 4,
|
||||
"end": 6,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 4
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 6
|
||||
}
|
||||
},
|
||||
"expressions": [],
|
||||
"quasis": [
|
||||
{
|
||||
"type": "TemplateElement",
|
||||
"start": 5,
|
||||
"end": 5,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 5
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 5
|
||||
}
|
||||
},
|
||||
"value": {
|
||||
"raw": "",
|
||||
"cooked": ""
|
||||
},
|
||||
"tail": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"typeParameters": {
|
||||
"type": "TSTypeParameterInstantiation",
|
||||
"start": 1,
|
||||
"end": 4,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 1
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 4
|
||||
}
|
||||
},
|
||||
"params": [
|
||||
{
|
||||
"type": "TSTypeReference",
|
||||
"start": 2,
|
||||
"end": 3,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 2
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 3
|
||||
}
|
||||
},
|
||||
"typeName": {
|
||||
"type": "Identifier",
|
||||
"start": 2,
|
||||
"end": 3,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 2
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 3
|
||||
},
|
||||
"identifierName": "T"
|
||||
},
|
||||
"name": "T"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"directives": []
|
||||
}
|
||||
}
|
||||
@ -268,6 +268,10 @@ export default declare((api, { jsxPragma = "React" }) => {
|
||||
JSXOpeningElement(path) {
|
||||
path.node.typeParameters = null;
|
||||
},
|
||||
|
||||
TaggedTemplateExpression(path) {
|
||||
path.node.typeParameters = null;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -0,0 +1 @@
|
||||
f<T>``;
|
||||
@ -0,0 +1 @@
|
||||
f``;
|
||||
@ -510,6 +510,13 @@ defineType("TaggedTemplateExpression", {
|
||||
quasi: {
|
||||
validate: assertNodeType("TemplateLiteral"),
|
||||
},
|
||||
typeParameters: {
|
||||
validate: assertNodeType(
|
||||
"TypeParameterInstantiation",
|
||||
"TSTypeParameterInstantiation",
|
||||
),
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user