diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 9bbed6ca25f1..224515cd76e2 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -636,6 +636,16 @@ export default class StatementParser extends ExpressionParser { return this.finishNode(node, "ThrowStatement"); } + parseCatchClauseParam(): N.Identifier { + const param = this.parseBindingAtom(); + + const simple = param.type === "Identifier"; + this.scope.enter(simple ? SCOPE_SIMPLE_CATCH : 0); + this.checkLVal(param, BIND_LEXICAL, null, "catch clause"); + + return param; + } + parseTryStatement(node: N.TryStatement): N.TryStatement { this.next(); @@ -647,10 +657,7 @@ export default class StatementParser extends ExpressionParser { this.next(); if (this.match(tt.parenL)) { this.expect(tt.parenL); - clause.param = this.parseBindingAtom(); - const simple = clause.param.type === "Identifier"; - this.scope.enter(simple ? SCOPE_SIMPLE_CATCH : 0); - this.checkLVal(clause.param, BIND_LEXICAL, null, "catch clause"); + clause.param = this.parseCatchClauseParam(); this.expect(tt.parenR); } else { clause.param = null; diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 6309c8ea8fcb..64b5fa393db3 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -86,6 +86,8 @@ const TSErrors = Object.freeze({ "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`", + UnexpectedCatchClauseParamType: + "Catch clause variables can only be typed 'any' or 'unknown'", UnexpectedReadonly: "'readonly' type modifier is only permitted on array and tuple literal types.", UnexpectedTypeAnnotation: "Did not expect a type annotation here.", @@ -2667,4 +2669,26 @@ export default (superClass: Class): Class => return hasContextParam ? baseCount + 1 : baseCount; } + + parseCatchClauseParam(): N.Identifier { + const param = super.parseCatchClauseParam(); + const type = this.tsTryParseTypeAnnotation(); + + if (type) { + switch (type.typeAnnotation.type) { + case "TSAnyKeyword": + case "TSUnknownKeyword": + param.type = type; + break; + + default: + this.raise( + type.typeAnnotation.start, + TSErrors.UnexpectedCatchClauseParamType, + ); + } + } + + return param; + } }; diff --git a/packages/babel-parser/test/fixtures/typescript/catch-clause/invalid-type/input.ts b/packages/babel-parser/test/fixtures/typescript/catch-clause/invalid-type/input.ts new file mode 100644 index 000000000000..8885a7ce001c --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/catch-clause/invalid-type/input.ts @@ -0,0 +1 @@ +try {} catch (ex: string) {} diff --git a/packages/babel-parser/test/fixtures/typescript/catch-clause/invalid-type/output.json b/packages/babel-parser/test/fixtures/typescript/catch-clause/invalid-type/output.json new file mode 100644 index 000000000000..eee85c129ed7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/catch-clause/invalid-type/output.json @@ -0,0 +1,42 @@ +{ + "type": "File", + "start":0,"end":28,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":28}}, + "errors": [ + "SyntaxError: Catch clause variables can only be typed 'any' or 'unknown' (1:18)" + ], + "program": { + "type": "Program", + "start":0,"end":28,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":28}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TryStatement", + "start":0,"end":28,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":28}}, + "block": { + "type": "BlockStatement", + "start":4,"end":6,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":6}}, + "body": [], + "directives": [] + }, + "handler": { + "type": "CatchClause", + "start":7,"end":28,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":28}}, + "param": { + "type": "Identifier", + "start":14,"end":16,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":16},"identifierName":"ex"}, + "name": "ex" + }, + "body": { + "type": "BlockStatement", + "start":26,"end":28,"loc":{"start":{"line":1,"column":26},"end":{"line":1,"column":28}}, + "body": [], + "directives": [] + } + }, + "finalizer": null + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/catch-clause/unknown/input.ts b/packages/babel-parser/test/fixtures/typescript/catch-clause/unknown/input.ts new file mode 100644 index 000000000000..a64f98e4bcc5 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/catch-clause/unknown/input.ts @@ -0,0 +1,3 @@ +try {} catch (ex) {} +try {} catch (ex: unknown) {} +try {} catch (ex: any) {} diff --git a/packages/babel-parser/test/fixtures/typescript/catch-clause/unknown/output.json b/packages/babel-parser/test/fixtures/typescript/catch-clause/unknown/output.json new file mode 100644 index 000000000000..092fff74a5b7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/catch-clause/unknown/output.json @@ -0,0 +1,105 @@ +{ + "type": "File", + "start":0,"end":76,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":25}}, + "program": { + "type": "Program", + "start":0,"end":76,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":25}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TryStatement", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":20}}, + "block": { + "type": "BlockStatement", + "start":4,"end":6,"loc":{"start":{"line":1,"column":4},"end":{"line":1,"column":6}}, + "body": [], + "directives": [] + }, + "handler": { + "type": "CatchClause", + "start":7,"end":20,"loc":{"start":{"line":1,"column":7},"end":{"line":1,"column":20}}, + "param": { + "type": "Identifier", + "start":14,"end":16,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":16},"identifierName":"ex"}, + "name": "ex" + }, + "body": { + "type": "BlockStatement", + "start":18,"end":20,"loc":{"start":{"line":1,"column":18},"end":{"line":1,"column":20}}, + "body": [], + "directives": [] + } + }, + "finalizer": null + }, + { + "type": "TryStatement", + "start":21,"end":50,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":29}}, + "block": { + "type": "BlockStatement", + "start":25,"end":27,"loc":{"start":{"line":2,"column":4},"end":{"line":2,"column":6}}, + "body": [], + "directives": [] + }, + "handler": { + "type": "CatchClause", + "start":28,"end":50,"loc":{"start":{"line":2,"column":7},"end":{"line":2,"column":29}}, + "param": { + "type": { + "type": "TSTypeAnnotation", + "start":37,"end":46,"loc":{"start":{"line":2,"column":16},"end":{"line":2,"column":25}}, + "typeAnnotation": { + "type": "TSUnknownKeyword", + "start":39,"end":46,"loc":{"start":{"line":2,"column":18},"end":{"line":2,"column":25}} + } + }, + "start":35,"end":37,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":16},"identifierName":"ex"}, + "name": "ex" + }, + "body": { + "type": "BlockStatement", + "start":48,"end":50,"loc":{"start":{"line":2,"column":27},"end":{"line":2,"column":29}}, + "body": [], + "directives": [] + } + }, + "finalizer": null + }, + { + "type": "TryStatement", + "start":51,"end":76,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":25}}, + "block": { + "type": "BlockStatement", + "start":55,"end":57,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":6}}, + "body": [], + "directives": [] + }, + "handler": { + "type": "CatchClause", + "start":58,"end":76,"loc":{"start":{"line":3,"column":7},"end":{"line":3,"column":25}}, + "param": { + "type": { + "type": "TSTypeAnnotation", + "start":67,"end":72,"loc":{"start":{"line":3,"column":16},"end":{"line":3,"column":21}}, + "typeAnnotation": { + "type": "TSAnyKeyword", + "start":69,"end":72,"loc":{"start":{"line":3,"column":18},"end":{"line":3,"column":21}} + } + }, + "start":65,"end":67,"loc":{"start":{"line":3,"column":14},"end":{"line":3,"column":16},"identifierName":"ex"}, + "name": "ex" + }, + "body": { + "type": "BlockStatement", + "start":74,"end":76,"loc":{"start":{"line":3,"column":23},"end":{"line":3,"column":25}}, + "body": [], + "directives": [] + } + }, + "finalizer": null + } + ], + "directives": [] + } +} \ No newline at end of file