From 05bf6d34ef996c4e055b2ef890d7ebdbb77217bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Thu, 15 Oct 2020 10:36:15 +0200 Subject: [PATCH 1/2] Reland "Fix: check if param is assignable when parsing arrow return type annotation" This reverts commit 91a7a64b4b0c30ab1fdfbb12f77522afa2285ec4. --- .../babel-parser/src/parser/expression.js | 7 +- packages/babel-parser/src/parser/lval.js | 53 ++- packages/babel-parser/src/plugins/flow.js | 51 +-- .../src/plugins/typescript/index.js | 23 +- .../input.ts | 1 + .../options.json | 7 + .../return-type-like-conditional/input.ts | 4 + .../return-type-like-conditional/options.json | 3 + .../return-type-like-conditional/output.json | 309 ++++++++++++++++++ 9 files changed, 411 insertions(+), 47 deletions(-) create mode 100644 packages/babel-parser/test/fixtures/typescript/arrow-function/ambiguous-ternary-arrow-return-type-typescript-issue-16241/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/arrow-function/ambiguous-ternary-arrow-return-type-typescript-issue-16241/options.json create mode 100644 packages/babel-parser/test/fixtures/typescript/arrow-function/return-type-like-conditional/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/arrow-function/return-type-like-conditional/options.json create mode 100644 packages/babel-parser/test/fixtures/typescript/arrow-function/return-type-like-conditional/output.json diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 559a4b58e1f9..ad8ad9ccb225 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -1453,7 +1453,7 @@ export default class ExpressionParser extends LValParser { if ( canBeArrow && this.shouldParseArrow() && - (arrowNode = this.parseArrow(arrowNode)) + (arrowNode = this.parseArrow(arrowNode, exprList)) ) { if (!this.isAwaitAllowed() && !this.state.maybeInAsyncArrowHead) { this.state.awaitPos = oldAwaitPos; @@ -1509,7 +1509,10 @@ export default class ExpressionParser extends LValParser { return !this.canInsertSemicolon(); } - parseArrow(node: N.ArrowFunctionExpression): ?N.ArrowFunctionExpression { + parseArrow( + node: N.ArrowFunctionExpression, + exprList: N.Node[], // eslint-disable-line no-unused-vars + ): ?N.ArrowFunctionExpression { if (this.eat(tt.arrow)) { return node; } diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 42c094b76203..481c6744cef3 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -51,10 +51,59 @@ export default class LValParser extends NodeUtils { +parseDecorator: () => Decorator; */ + /** + * Check if a node can be converted to a binding identifier or binding pattern. + * https://tc39.es/ecma262/#prod-BindingIdentifier + * https://tc39.es/ecma262/#prod-BindingPattern + * Note that although a mebmer expression can serve as a LHS in the init of for loop, + * i.e. `for (a.b of []);`, it is not a binding pattern + * + * @param {Node} node + * @returns {boolean} + * @memberof LValParser + */ + isAssignable(node: Node): boolean { + switch (node.type) { + case "Identifier": + case "ObjectPattern": + case "ArrayPattern": + case "AssignmentPattern": + return true; + + case "ObjectExpression": { + const last = node.properties.length - 1; + return node.properties.every((prop, i) => { + return ( + prop.type !== "ObjectMethod" && + (i === last || prop.type === "SpreadElement") && + this.isAssignable(prop) + ); + }); + } + + case "ObjectProperty": + return this.isAssignable(node.value); + + case "SpreadElement": + return this.isAssignable(node.argument); + + case "ArrayExpression": + return node.elements.every(element => this.isAssignable(element)); + + case "AssignmentExpression": + return node.operator === "="; + + case "ParenthesizedExpression": + return this.isAssignable(node.expression); + + default: + return false; + } + } + // Convert existing expression atom to assignable pattern // if possible. - // NOTE: There is a corresponding "isAssignable" method in flow.js. - // When this one is updated, please check if also that one needs to be updated. + // When this one is updated, please check if `isAssignable` also needs to be updated. toAssignable(node: Node): Node { let parenthesized = undefined; diff --git a/packages/babel-parser/src/plugins/flow.js b/packages/babel-parser/src/plugins/flow.js index 26ddfeaae787..17da0a7fe117 100644 --- a/packages/babel-parser/src/plugins/flow.js +++ b/packages/babel-parser/src/plugins/flow.js @@ -1943,7 +1943,7 @@ export default (superClass: Class): Class => } return partition(arrows, node => - node.params.every(param => this.isAssignable(param, true)), + node.params.every(param => this.isAssignable(param)), ); } @@ -2146,47 +2146,12 @@ export default (superClass: Class): Class => } } - isAssignable(node: N.Node, isBinding?: boolean): boolean { + isAssignable(node: N.Node): boolean { switch (node.type) { - case "Identifier": - case "ObjectPattern": - case "ArrayPattern": - case "AssignmentPattern": - return true; - - case "ObjectExpression": { - const last = node.properties.length - 1; - return node.properties.every((prop, i) => { - return ( - prop.type !== "ObjectMethod" && - (i === last || prop.type === "SpreadElement") && - this.isAssignable(prop) - ); - }); - } - - case "ObjectProperty": - return this.isAssignable(node.value); - - case "SpreadElement": - return this.isAssignable(node.argument); - - case "ArrayExpression": - return node.elements.every(element => this.isAssignable(element)); - - case "AssignmentExpression": - return node.operator === "="; - - case "ParenthesizedExpression": case "TypeCastExpression": return this.isAssignable(node.expression); - - case "MemberExpression": - case "OptionalMemberExpression": - return !isBinding; - default: - return false; + return super.isAssignable(node); } } @@ -2772,7 +2737,10 @@ export default (superClass: Class): Class => } // handle return types for arrow functions - parseArrow(node: N.ArrowFunctionExpression): ?N.ArrowFunctionExpression { + parseArrow( + node: N.ArrowFunctionExpression, + exprList: N.Node[], + ): ?N.ArrowFunctionExpression { if (this.match(tt.colon)) { const result = this.tryParse(() => { const oldNoAnonFunctionType = this.state.noAnonFunctionType; @@ -2806,7 +2774,7 @@ export default (superClass: Class): Class => : null; } - return super.parseArrow(node); + return super.parseArrow(node, exprList); } shouldParseArrow(): boolean { @@ -2977,7 +2945,8 @@ export default (superClass: Class): Class => ): ?N.ArrowFunctionExpression { const node = this.startNodeAt(startPos, startLoc); this.parseFunctionParams(node); - if (!this.parseArrow(node)) return; + // set exprList to `[]` as the parameters has been validated in `parseFunctionParams` + if (!this.parseArrow(node, [])) return; return this.parseArrowExpression( node, /* params */ undefined, diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index b85c41371a68..e1f27f3ce913 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -2540,7 +2540,10 @@ export default (superClass: Class): Class => } } - parseArrow(node: N.ArrowFunctionExpression): ?N.ArrowFunctionExpression { + parseArrow( + node: N.ArrowFunctionExpression, + exprList, + ): ?N.ArrowFunctionExpression { if (this.match(tt.colon)) { // This is different from how the TS parser does it. // TS uses lookahead. The Babel Parser parses it as a parenthesized expression and converts. @@ -2550,6 +2553,10 @@ export default (superClass: Class): Class => tt.colon, ); if (this.canInsertSemicolon() || !this.match(tt.arrow)) abort(); + // check if the exprList is assignable because `: TSType` can be part of conditional expression + // i.e. we can only know `: v` is not a return type by checking that `sum(v)` can not be a pattern. + // 0 ? v => (sum(v)) : v => 0 + if (exprList.some(param => !this.isAssignable(param))) abort(); return returnType; }); @@ -2561,7 +2568,7 @@ export default (superClass: Class): Class => } } - return super.parseArrow(node); + return super.parseArrow(node, exprList); } // Allow type annotations inside of a parameter list. @@ -2580,6 +2587,18 @@ export default (superClass: Class): Class => return param; } + isAssignable(node: N.Node): boolean { + switch (node.type) { + case "TSAsExpression": + case "TSNonNullExpression": + case "TSTypeAssertion": + case "TSTypeCastExpression": + return this.isAssignable(node.expression); + default: + return super.isAssignable(node); + } + } + toAssignable(node: N.Node): N.Node { switch (node.type) { case "TSTypeCastExpression": diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/ambiguous-ternary-arrow-return-type-typescript-issue-16241/input.ts b/packages/babel-parser/test/fixtures/typescript/arrow-function/ambiguous-ternary-arrow-return-type-typescript-issue-16241/input.ts new file mode 100644 index 000000000000..ebcb43495f37 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/ambiguous-ternary-arrow-return-type-typescript-issue-16241/input.ts @@ -0,0 +1 @@ +0 ? v => (v) : v => 0; diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/ambiguous-ternary-arrow-return-type-typescript-issue-16241/options.json b/packages/babel-parser/test/fixtures/typescript/arrow-function/ambiguous-ternary-arrow-return-type-typescript-issue-16241/options.json new file mode 100644 index 000000000000..0c7f7817f9f7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/ambiguous-ternary-arrow-return-type-typescript-issue-16241/options.json @@ -0,0 +1,7 @@ +{ + "sourceType": "module", + "plugins": [ + "typescript" + ], + "throws": "Unexpected token, expected \":\" (1:21)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/return-type-like-conditional/input.ts b/packages/babel-parser/test/fixtures/typescript/arrow-function/return-type-like-conditional/input.ts new file mode 100644 index 000000000000..f74bd2c8ea88 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/return-type-like-conditional/input.ts @@ -0,0 +1,4 @@ +0 ? v => (sum += v) : v => 0; +0 ? v => (sum(v)) : v => 0; +0 ? v => (v.w) : v => 0; +0 ? v => ([ v.w ]) : v => {}; diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/return-type-like-conditional/options.json b/packages/babel-parser/test/fixtures/typescript/arrow-function/return-type-like-conditional/options.json new file mode 100644 index 000000000000..5047d6993fe8 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/return-type-like-conditional/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["typescript"] +} diff --git a/packages/babel-parser/test/fixtures/typescript/arrow-function/return-type-like-conditional/output.json b/packages/babel-parser/test/fixtures/typescript/arrow-function/return-type-like-conditional/output.json new file mode 100644 index 000000000000..08bbc5b1e9d0 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/arrow-function/return-type-like-conditional/output.json @@ -0,0 +1,309 @@ +{ + "type": "File", + "start":0,"end":112,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":29}}, + "program": { + "type": "Program", + "start":0,"end":112,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":29}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":29}}, + "expression": { + "type": "ConditionalExpression", + "start":0,"end":28,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":28}}, + "test": { + "type": "NumericLiteral", + "start":0,"end":1,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":1}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start":4,"end":19,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":19}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":4,"end":5,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":5},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "AssignmentExpression", + "start":10,"end":18,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":18}}, + "operator": "+=", + "left": { + "type": "Identifier", + "start":10,"end":13,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":13},"identifierName":"sum"}, + "name": "sum" + }, + "right": { + "type": "Identifier", + "start":17,"end":18,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":18},"identifierName":"v"}, + "name": "v" + }, + "extra": { + "parenthesized": true, + "parenStart": 9 + } + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start":22,"end":28,"loc":{"start":{"line":1,"column":22},"end":{"line":1,"column":28}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":22,"end":23,"loc":{"start":{"line":1,"column":22},"end":{"line":1,"column":23},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "NumericLiteral", + "start":27,"end":28,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":28}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + } + } + } + }, + { + "type": "ExpressionStatement", + "start":30,"end":57,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":27}}, + "expression": { + "type": "ConditionalExpression", + "start":30,"end":56,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":26}}, + "test": { + "type": "NumericLiteral", + "start":30,"end":31,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":1}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start":34,"end":47,"loc":{"start":{"line":2,"column":4},"end":{"line":2,"column":17}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":34,"end":35,"loc":{"start":{"line":2,"column":4},"end":{"line":2,"column":5},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "CallExpression", + "start":40,"end":46,"loc":{"start":{"line":2,"column":10},"end":{"line":2,"column":16}}, + "callee": { + "type": "Identifier", + "start":40,"end":43,"loc":{"start":{"line":2,"column":10},"end":{"line":2,"column":13},"identifierName":"sum"}, + "name": "sum" + }, + "arguments": [ + { + "type": "Identifier", + "start":44,"end":45,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":15},"identifierName":"v"}, + "name": "v" + } + ], + "extra": { + "parenthesized": true, + "parenStart": 39 + } + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start":50,"end":56,"loc":{"start":{"line":2,"column":20},"end":{"line":2,"column":26}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":50,"end":51,"loc":{"start":{"line":2,"column":20},"end":{"line":2,"column":21},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "NumericLiteral", + "start":55,"end":56,"loc":{"start":{"line":2,"column":25},"end":{"line":2,"column":26}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + } + } + } + }, + { + "type": "ExpressionStatement", + "start":58,"end":82,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":24}}, + "expression": { + "type": "ConditionalExpression", + "start":58,"end":81,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":23}}, + "test": { + "type": "NumericLiteral", + "start":58,"end":59,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":1}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start":62,"end":72,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":14}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":62,"end":63,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":5},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "MemberExpression", + "start":68,"end":71,"loc":{"start":{"line":3,"column":10},"end":{"line":3,"column":13}}, + "object": { + "type": "Identifier", + "start":68,"end":69,"loc":{"start":{"line":3,"column":10},"end":{"line":3,"column":11},"identifierName":"v"}, + "name": "v" + }, + "computed": false, + "property": { + "type": "Identifier", + "start":70,"end":71,"loc":{"start":{"line":3,"column":12},"end":{"line":3,"column":13},"identifierName":"w"}, + "name": "w" + }, + "extra": { + "parenthesized": true, + "parenStart": 67 + } + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start":75,"end":81,"loc":{"start":{"line":3,"column":17},"end":{"line":3,"column":23}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":75,"end":76,"loc":{"start":{"line":3,"column":17},"end":{"line":3,"column":18},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "NumericLiteral", + "start":80,"end":81,"loc":{"start":{"line":3,"column":22},"end":{"line":3,"column":23}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + } + } + } + }, + { + "type": "ExpressionStatement", + "start":83,"end":112,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":29}}, + "expression": { + "type": "ConditionalExpression", + "start":83,"end":111,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":28}}, + "test": { + "type": "NumericLiteral", + "start":83,"end":84,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":1}}, + "extra": { + "rawValue": 0, + "raw": "0" + }, + "value": 0 + }, + "consequent": { + "type": "ArrowFunctionExpression", + "start":87,"end":101,"loc":{"start":{"line":4,"column":4},"end":{"line":4,"column":18}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":87,"end":88,"loc":{"start":{"line":4,"column":4},"end":{"line":4,"column":5},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "ArrayExpression", + "start":93,"end":100,"loc":{"start":{"line":4,"column":10},"end":{"line":4,"column":17}}, + "elements": [ + { + "type": "MemberExpression", + "start":95,"end":98,"loc":{"start":{"line":4,"column":12},"end":{"line":4,"column":15}}, + "object": { + "type": "Identifier", + "start":95,"end":96,"loc":{"start":{"line":4,"column":12},"end":{"line":4,"column":13},"identifierName":"v"}, + "name": "v" + }, + "computed": false, + "property": { + "type": "Identifier", + "start":97,"end":98,"loc":{"start":{"line":4,"column":14},"end":{"line":4,"column":15},"identifierName":"w"}, + "name": "w" + } + } + ], + "extra": { + "parenthesized": true, + "parenStart": 92 + } + } + }, + "alternate": { + "type": "ArrowFunctionExpression", + "start":104,"end":111,"loc":{"start":{"line":4,"column":21},"end":{"line":4,"column":28}}, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start":104,"end":105,"loc":{"start":{"line":4,"column":21},"end":{"line":4,"column":22},"identifierName":"v"}, + "name": "v" + } + ], + "body": { + "type": "BlockStatement", + "start":109,"end":111,"loc":{"start":{"line":4,"column":26},"end":{"line":4,"column":28}}, + "body": [], + "directives": [] + } + } + } + } + ], + "directives": [] + } +} \ No newline at end of file From 99f9a101e98d92cfe3172ea85cd077a11d185cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Thu, 15 Oct 2020 16:24:04 +0200 Subject: [PATCH 2/2] Mark `RestElement` as assignable --- packages/babel-parser/src/parser/lval.js | 1 + .../regression/rest-in-arrow/input.ts | 1 + .../regression/rest-in-arrow/output.json | 61 +++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 packages/babel-parser/test/fixtures/typescript/regression/rest-in-arrow/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/regression/rest-in-arrow/output.json diff --git a/packages/babel-parser/src/parser/lval.js b/packages/babel-parser/src/parser/lval.js index 481c6744cef3..19fc2f06e62b 100644 --- a/packages/babel-parser/src/parser/lval.js +++ b/packages/babel-parser/src/parser/lval.js @@ -68,6 +68,7 @@ export default class LValParser extends NodeUtils { case "ObjectPattern": case "ArrayPattern": case "AssignmentPattern": + case "RestElement": return true; case "ObjectExpression": { diff --git a/packages/babel-parser/test/fixtures/typescript/regression/rest-in-arrow/input.ts b/packages/babel-parser/test/fixtures/typescript/regression/rest-in-arrow/input.ts new file mode 100644 index 000000000000..5ceec7855490 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/regression/rest-in-arrow/input.ts @@ -0,0 +1 @@ +const f = (...args): void => {}; diff --git a/packages/babel-parser/test/fixtures/typescript/regression/rest-in-arrow/output.json b/packages/babel-parser/test/fixtures/typescript/regression/rest-in-arrow/output.json new file mode 100644 index 000000000000..fabbad620bdc --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/regression/rest-in-arrow/output.json @@ -0,0 +1,61 @@ +{ + "type": "File", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "program": { + "type": "Program", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":6,"end":31,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":31}}, + "id": { + "type": "Identifier", + "start":6,"end":7,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":7},"identifierName":"f"}, + "name": "f" + }, + "init": { + "type": "ArrowFunctionExpression", + "start":10,"end":31,"loc":{"start":{"line":1,"column":10},"end":{"line":1,"column":31}}, + "returnType": { + "type": "TSTypeAnnotation", + "start":19,"end":25,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":25}}, + "typeAnnotation": { + "type": "TSVoidKeyword", + "start":21,"end":25,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":25}} + } + }, + "id": null, + "generator": false, + "async": false, + "params": [ + { + "type": "RestElement", + "start":11,"end":18,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":18}}, + "argument": { + "type": "Identifier", + "start":14,"end":18,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":18},"identifierName":"args"}, + "name": "args" + } + } + ], + "body": { + "type": "BlockStatement", + "start":29,"end":31,"loc":{"start":{"line":1,"column":29},"end":{"line":1,"column":31}}, + "body": [], + "directives": [] + } + } + } + ], + "kind": "const" + } + ], + "directives": [] + } +} \ No newline at end of file