diff --git a/packages/babel-parser/src/plugins/typescript.js b/packages/babel-parser/src/plugins/typescript.js index 1a0c8c7a65..07e57b9914 100644 --- a/packages/babel-parser/src/plugins/typescript.js +++ b/packages/babel-parser/src/plugins/typescript.js @@ -905,11 +905,21 @@ export default (superClass: Class): Class => return this.finishNode(node, "TSTypeAssertion"); } - tsParseHeritageClause(): $ReadOnlyArray { - return this.tsParseDelimitedList( + tsParseHeritageClause( + descriptor: string, + ): $ReadOnlyArray { + const originalStart = this.state.start; + + const delimitedList = this.tsParseDelimitedList( "HeritageClauseElement", this.tsParseExpressionWithTypeArguments.bind(this), ); + + if (!delimitedList.length) { + this.raise(originalStart, `'${descriptor}' list cannot be empty.`); + } + + return delimitedList; } tsParseExpressionWithTypeArguments(): N.TsExpressionWithTypeArguments { @@ -930,7 +940,7 @@ export default (superClass: Class): Class => node.id = this.parseIdentifier(); node.typeParameters = this.tsTryParseTypeParameters(); if (this.eat(tt._extends)) { - node.extends = this.tsParseHeritageClause(); + node.extends = this.tsParseHeritageClause("extends"); } const body: N.TSInterfaceBody = this.startNode(); body.body = this.tsInType(this.tsParseObjectTypeMembers.bind(this)); @@ -1879,7 +1889,7 @@ export default (superClass: Class): Class => node.superTypeParameters = this.tsParseTypeArguments(); } if (this.eatContextual("implements")) { - node.implements = this.tsParseHeritageClause(); + node.implements = this.tsParseHeritageClause("implements"); } } diff --git a/packages/babel-parser/test/fixtures/typescript/class/extends-empty/input.js b/packages/babel-parser/test/fixtures/typescript/class/extends-empty/input.js new file mode 100644 index 0000000000..8b826e7919 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/extends-empty/input.js @@ -0,0 +1,2 @@ +interface foo extends { +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/extends-empty/options.json b/packages/babel-parser/test/fixtures/typescript/class/extends-empty/options.json new file mode 100644 index 0000000000..3de0ab8596 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/extends-empty/options.json @@ -0,0 +1,3 @@ +{ + "throws": "'extends' list cannot be empty. (1:22)" +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/extends-implements-empty/input.js b/packages/babel-parser/test/fixtures/typescript/class/extends-implements-empty/input.js new file mode 100644 index 0000000000..aa9ed6dca2 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/extends-implements-empty/input.js @@ -0,0 +1,2 @@ +class Foo extends Bar implements { +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/extends-implements-empty/options.json b/packages/babel-parser/test/fixtures/typescript/class/extends-implements-empty/options.json new file mode 100644 index 0000000000..9505064468 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/extends-implements-empty/options.json @@ -0,0 +1,3 @@ +{ + "throws": "'implements' list cannot be empty. (1:33)" +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/implements-empty/input.js b/packages/babel-parser/test/fixtures/typescript/class/implements-empty/input.js new file mode 100644 index 0000000000..f72f480995 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/implements-empty/input.js @@ -0,0 +1,2 @@ +class Foo implements { +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/implements-empty/options.json b/packages/babel-parser/test/fixtures/typescript/class/implements-empty/options.json new file mode 100644 index 0000000000..6751fbcc54 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/implements-empty/options.json @@ -0,0 +1,3 @@ +{ + "throws": "'implements' list cannot be empty. (1:21)" +} diff --git a/packages/babel-parser/test/fixtures/typescript/type-arguments/empty/input.js b/packages/babel-parser/test/fixtures/typescript/type-arguments/empty/input.js new file mode 100644 index 0000000000..63cf8bbeec --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-arguments/empty/input.js @@ -0,0 +1 @@ +foo<>() diff --git a/packages/babel-parser/test/fixtures/typescript/type-arguments/empty/output.json b/packages/babel-parser/test/fixtures/typescript/type-arguments/empty/output.json new file mode 100644 index 0000000000..ce11198360 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-arguments/empty/output.json @@ -0,0 +1,99 @@ +{ + "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": "CallExpression", + "start": 0, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "callee": { + "type": "Identifier", + "start": 0, + "end": 3, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 3 + }, + "identifierName": "foo" + }, + "name": "foo" + }, + "arguments": [], + "typeParameters": { + "type": "TSTypeParameterInstantiation", + "start": 3, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 3 + }, + "end": { + "line": 1, + "column": 5 + } + }, + "params": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file