diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 9447dd03256e..48d19ad3fbb0 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -89,6 +89,8 @@ const TSErrors = Object.freeze({ "Tuple members must all have names or all not have names.", NonAbstractClassHasAbstractMethod: "Abstract methods can only appear within an abstract class.", + NonClassMethodPropertyHasAbstractModifer: + "'abstract' modifier can only appear on a class, method, or property declaration.", OptionalTypeBeforeRequired: "A required element cannot follow an optional element.", PatternIsOptional: @@ -1585,20 +1587,13 @@ export default (superClass: Class): Class => ): ?N.Declaration { switch (value) { case "abstract": - if (this.tsCheckLineTerminatorAndMatch(tt._class, next)) { - const cls: N.ClassDeclaration = node; - cls.abstract = true; - if (next) { - this.next(); - if (!this.match(tt._class)) { - this.unexpected(null, tt._class); - } - } - return this.parseClass( - cls, - /* isStatement */ true, - /* optionalId */ false, - ); + if ( + this.tsCheckLineTerminatorAndMatch(tt._class, next) || + // for interface + this.tsCheckLineTerminatorAndMatch(tt.name, next) + ) { + if (next) this.next(); + return this.tsParseAbstractDeclaration(node); } break; @@ -2849,4 +2844,31 @@ export default (superClass: Class): Class => this.state.inAbstractClass = oldInAbstractClass; } } + + tsParseAbstractDeclaration( + node: any, + ): N.ClassDeclaration | N.TsInterfaceDeclaration { + const isClass = this.match(tt._class); + if (!isClass && !this.isContextual("interface")) { + this.unexpected(null, tt._class); + } + node.abstract = true; + if (isClass) { + return this.parseClass( + (node: N.ClassDeclaration), + /* isStatement */ true, + /* optionalId */ false, + ); + } else { + // for invalid abstract interface + this.raise( + node.start, + TSErrors.NonClassMethodPropertyHasAbstractModifer, + ); + this.next(); + return this.tsParseInterfaceDeclaration( + (node: N.TsInterfaceDeclaration), + ); + } + } }; diff --git a/packages/babel-parser/test/fixtures/typescript/interface/abstract/input.ts b/packages/babel-parser/test/fixtures/typescript/interface/abstract/input.ts new file mode 100644 index 000000000000..38a2e02394ad --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/interface/abstract/input.ts @@ -0,0 +1,3 @@ +abstract interface Foo { + foo: string; +} diff --git a/packages/babel-parser/test/fixtures/typescript/interface/abstract/output.json b/packages/babel-parser/test/fixtures/typescript/interface/abstract/output.json new file mode 100644 index 000000000000..dd5eecede71b --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/interface/abstract/output.json @@ -0,0 +1,50 @@ +{ + "type": "File", + "start":0,"end":41,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "errors": [ + "SyntaxError: 'abstract' modifier can only appear on a class, method, or property declaration. (1:0)" + ], + "program": { + "type": "Program", + "start":0,"end":41,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TSInterfaceDeclaration", + "start":0,"end":41,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "abstract": true, + "id": { + "type": "Identifier", + "start":19,"end":22,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":22},"identifierName":"Foo"}, + "name": "Foo" + }, + "body": { + "type": "TSInterfaceBody", + "start":23,"end":41,"loc":{"start":{"line":1,"column":23},"end":{"line":3,"column":1}}, + "body": [ + { + "type": "TSPropertySignature", + "start":27,"end":39,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":14}}, + "key": { + "type": "Identifier", + "start":27,"end":30,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":5},"identifierName":"foo"}, + "name": "foo" + }, + "computed": false, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start":30,"end":38,"loc":{"start":{"line":2,"column":5},"end":{"line":2,"column":13}}, + "typeAnnotation": { + "type": "TSStringKeyword", + "start":32,"end":38,"loc":{"start":{"line":2,"column":7},"end":{"line":2,"column":13}} + } + } + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/interface/export-abstract-interface/options.json b/packages/babel-parser/test/fixtures/typescript/interface/export-abstract-interface/options.json deleted file mode 100644 index e94c31509714..000000000000 --- a/packages/babel-parser/test/fixtures/typescript/interface/export-abstract-interface/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Unexpected token, expected \"class\" (1:16)" -} diff --git a/packages/babel-parser/test/fixtures/typescript/interface/export-abstract-interface/output.json b/packages/babel-parser/test/fixtures/typescript/interface/export-abstract-interface/output.json new file mode 100644 index 000000000000..d1172b3d8d5e --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/interface/export-abstract-interface/output.json @@ -0,0 +1,38 @@ +{ + "type": "File", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "errors": [ + "SyntaxError: 'abstract' modifier can only appear on a class, method, or property declaration. (1:7)" + ], + "program": { + "type": "Program", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExportNamedDeclaration", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "exportKind": "type", + "specifiers": [], + "source": null, + "declaration": { + "type": "TSInterfaceDeclaration", + "start":7,"end":32,"loc":{"start":{"line":1,"column":7},"end":{"line":3,"column":1}}, + "abstract": true, + "id": { + "type": "Identifier", + "start":26,"end":27,"loc":{"start":{"line":1,"column":26},"end":{"line":1,"column":27},"identifierName":"I"}, + "name": "I" + }, + "body": { + "type": "TSInterfaceBody", + "start":28,"end":32,"loc":{"start":{"line":1,"column":28},"end":{"line":3,"column":1}}, + "body": [] + } + } + } + ], + "directives": [] + } +} \ No newline at end of file