From 1d1fab4ea22ee84703e860dbe9360c94190fe26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Tue, 29 Oct 2019 18:27:54 +0100 Subject: [PATCH] [parser] Add support for private fields in TypeScript (#10483) * [parser] Add support for private fields in TypeScript * Fix flow --- .../src/plugins/typescript/index.js | 33 +- packages/babel-parser/src/types.js | 9 +- .../fixtures/typescript/class/options.json | 2 +- .../private-fields-modifier-abstract/input.ts | 3 + .../options.json | 3 + .../private-fields-modifier-private/input.ts | 3 + .../options.json | 3 + .../input.ts | 3 + .../options.json | 3 + .../private-fields-modifier-public/input.ts | 3 + .../options.json | 3 + .../private-fields-modifier-readonly/input.ts | 4 + .../output.json | 215 +++++++++ .../class/private-fields-static/input.ts | 4 + .../class/private-fields-static/output.json | 213 +++++++++ .../typescript/class/private-fields/input.ts | 7 + .../class/private-fields/output.json | 424 ++++++++++++++++++ 17 files changed, 931 insertions(+), 4 deletions(-) create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-abstract/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-abstract/options.json create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-private/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-private/options.json create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-protected/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-protected/options.json create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-public/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-public/options.json create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-readonly/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-readonly/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields-static/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields-static/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/class/private-fields/output.json diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 8adf82f1568a..187dd540520a 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -1868,7 +1868,7 @@ export default (superClass: Class): Class => } parsePostMemberNameModifiers( - methodOrProp: N.ClassMethod | N.ClassProperty, + methodOrProp: N.ClassMethod | N.ClassProperty | N.ClassPrivateProperty, ): void { const optional = this.eat(tt.question); if (optional) methodOrProp.optional = true; @@ -2007,16 +2007,45 @@ export default (superClass: Class): Class => if (typeParameters) node.typeParameters = typeParameters; } - parseClassProperty(node: N.ClassProperty): N.ClassProperty { + parseClassPropertyAnnotation( + node: N.ClassProperty | N.ClassPrivateProperty, + ): void { if (!node.optional && this.eat(tt.bang)) { node.definite = true; } const type = this.tsTryParseTypeAnnotation(); if (type) node.typeAnnotation = type; + } + + parseClassProperty(node: N.ClassProperty): N.ClassProperty { + this.parseClassPropertyAnnotation(node); return super.parseClassProperty(node); } + parseClassPrivateProperty( + node: N.ClassPrivateProperty, + ): N.ClassPrivateProperty { + // $FlowIgnore + if (node.abstract) { + this.raise( + node.start, + "Private elements cannot have the 'abstract' modifier.", + ); + } + + // $FlowIgnore + if (node.accessibility) { + this.raise( + node.start, + `Private elements cannot have an accessibility modifier ('${node.accessibility}')`, + ); + } + + this.parseClassPropertyAnnotation(node); + return super.parseClassPrivateProperty(node); + } + pushClassMethod( classBody: N.ClassBody, method: N.ClassMethod, diff --git a/packages/babel-parser/src/types.js b/packages/babel-parser/src/types.js index 8560da3a3f73..f5b34a2db0a3 100644 --- a/packages/babel-parser/src/types.js +++ b/packages/babel-parser/src/types.js @@ -762,7 +762,14 @@ export type ClassPrivateProperty = NodeBase & { value: ?Expression, // TODO: Not in spec that this is nullable. static: boolean, computed: false, - typeAnnotation?: ?TypeAnnotation, // TODO: Not in spec + + // Flow and Typescript + typeAnnotation?: ?TypeAnnotationBase, + + // TypeScript only + optional?: true, + definite?: true, + readonly?: true, }; export type OptClassDeclaration = ClassBase & diff --git a/packages/babel-parser/test/fixtures/typescript/class/options.json b/packages/babel-parser/test/fixtures/typescript/class/options.json index 9f3a0c2c0ff1..fff045d3a491 100644 --- a/packages/babel-parser/test/fixtures/typescript/class/options.json +++ b/packages/babel-parser/test/fixtures/typescript/class/options.json @@ -1,4 +1,4 @@ { "sourceType": "module", - "plugins": ["typescript", "classProperties"] + "plugins": ["typescript", "classProperties", "classPrivateProperties"] } diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-abstract/input.ts b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-abstract/input.ts new file mode 100644 index 000000000000..7cf85ea5ec29 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-abstract/input.ts @@ -0,0 +1,3 @@ +abstract class A { + abstract #a; +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-abstract/options.json b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-abstract/options.json new file mode 100644 index 000000000000..7c4adceba22a --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-abstract/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Private elements cannot have the 'abstract' modifier. (2:2)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-private/input.ts b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-private/input.ts new file mode 100644 index 000000000000..a4472caceb86 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-private/input.ts @@ -0,0 +1,3 @@ +class A { + private #a; +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-private/options.json b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-private/options.json new file mode 100644 index 000000000000..a183ce2e7550 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-private/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Private elements cannot have an accessibility modifier ('private') (2:2)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-protected/input.ts b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-protected/input.ts new file mode 100644 index 000000000000..0f35b844a7be --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-protected/input.ts @@ -0,0 +1,3 @@ +class A { + protected #a; +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-protected/options.json b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-protected/options.json new file mode 100644 index 000000000000..e6c1bebe1ab9 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-protected/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Private elements cannot have an accessibility modifier ('protected') (2:2)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-public/input.ts b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-public/input.ts new file mode 100644 index 000000000000..59a236000e60 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-public/input.ts @@ -0,0 +1,3 @@ +class A { + public #a; +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-public/options.json b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-public/options.json new file mode 100644 index 000000000000..7fb90279eae1 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-public/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Private elements cannot have an accessibility modifier ('public') (2:2)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-readonly/input.ts b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-readonly/input.ts new file mode 100644 index 000000000000..355def79b8b7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-readonly/input.ts @@ -0,0 +1,4 @@ +class A { + readonly #a; + readonly #b: string; +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-readonly/output.json b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-readonly/output.json new file mode 100644 index 000000000000..f883e0b00b2a --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields-modifier-readonly/output.json @@ -0,0 +1,215 @@ +{ + "type": "File", + "start": 0, + "end": 49, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 49, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ClassDeclaration", + "start": 0, + "end": 49, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "id": { + "type": "Identifier", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + }, + "identifierName": "A" + }, + "name": "A" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 8, + "end": 49, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "body": [ + { + "type": "ClassProperty", + "start": 12, + "end": 24, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 14 + } + }, + "readonly": true, + "static": false, + "key": { + "type": "PrivateName", + "start": 21, + "end": 23, + "loc": { + "start": { + "line": 2, + "column": 11 + }, + "end": { + "line": 2, + "column": 13 + } + }, + "id": { + "type": "Identifier", + "start": 22, + "end": 23, + "loc": { + "start": { + "line": 2, + "column": 12 + }, + "end": { + "line": 2, + "column": 13 + }, + "identifierName": "a" + }, + "name": "a" + } + }, + "value": null + }, + { + "type": "ClassProperty", + "start": 27, + "end": 47, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 22 + } + }, + "readonly": true, + "static": false, + "key": { + "type": "PrivateName", + "start": 36, + "end": 38, + "loc": { + "start": { + "line": 3, + "column": 11 + }, + "end": { + "line": 3, + "column": 13 + } + }, + "id": { + "type": "Identifier", + "start": 37, + "end": 38, + "loc": { + "start": { + "line": 3, + "column": 12 + }, + "end": { + "line": 3, + "column": 13 + }, + "identifierName": "b" + }, + "name": "b" + } + }, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 38, + "end": 46, + "loc": { + "start": { + "line": 3, + "column": 13 + }, + "end": { + "line": 3, + "column": 21 + } + }, + "typeAnnotation": { + "type": "TSStringKeyword", + "start": 40, + "end": 46, + "loc": { + "start": { + "line": 3, + "column": 15 + }, + "end": { + "line": 3, + "column": 21 + } + } + } + }, + "value": null + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields-static/input.ts b/packages/babel-parser/test/fixtures/typescript/class/private-fields-static/input.ts new file mode 100644 index 000000000000..4725b09bea74 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields-static/input.ts @@ -0,0 +1,4 @@ +class A { + static #x; + static #y: string; +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields-static/output.json b/packages/babel-parser/test/fixtures/typescript/class/private-fields-static/output.json new file mode 100644 index 000000000000..dcb6ab198f52 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields-static/output.json @@ -0,0 +1,213 @@ +{ + "type": "File", + "start": 0, + "end": 45, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 45, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ClassDeclaration", + "start": 0, + "end": 45, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "id": { + "type": "Identifier", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + }, + "identifierName": "A" + }, + "name": "A" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 8, + "end": 45, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "body": [ + { + "type": "ClassPrivateProperty", + "start": 12, + "end": 22, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 12 + } + }, + "static": true, + "key": { + "type": "PrivateName", + "start": 19, + "end": 21, + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 11 + } + }, + "id": { + "type": "Identifier", + "start": 20, + "end": 21, + "loc": { + "start": { + "line": 2, + "column": 10 + }, + "end": { + "line": 2, + "column": 11 + }, + "identifierName": "x" + }, + "name": "x" + } + }, + "value": null + }, + { + "type": "ClassPrivateProperty", + "start": 25, + "end": 43, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 20 + } + }, + "static": true, + "key": { + "type": "PrivateName", + "start": 32, + "end": 34, + "loc": { + "start": { + "line": 3, + "column": 9 + }, + "end": { + "line": 3, + "column": 11 + } + }, + "id": { + "type": "Identifier", + "start": 33, + "end": 34, + "loc": { + "start": { + "line": 3, + "column": 10 + }, + "end": { + "line": 3, + "column": 11 + }, + "identifierName": "y" + }, + "name": "y" + } + }, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 34, + "end": 42, + "loc": { + "start": { + "line": 3, + "column": 11 + }, + "end": { + "line": 3, + "column": 19 + } + }, + "typeAnnotation": { + "type": "TSStringKeyword", + "start": 36, + "end": 42, + "loc": { + "start": { + "line": 3, + "column": 13 + }, + "end": { + "line": 3, + "column": 19 + } + } + } + }, + "value": null + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields/input.ts b/packages/babel-parser/test/fixtures/typescript/class/private-fields/input.ts new file mode 100644 index 000000000000..30c6024e99ce --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields/input.ts @@ -0,0 +1,7 @@ +class A { + #a: string; + #b?; + #c?: number; + #d!; + #e!: boolean; +} diff --git a/packages/babel-parser/test/fixtures/typescript/class/private-fields/output.json b/packages/babel-parser/test/fixtures/typescript/class/private-fields/output.json new file mode 100644 index 000000000000..67a50c2c16ff --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/class/private-fields/output.json @@ -0,0 +1,424 @@ +{ + "type": "File", + "start": 0, + "end": 70, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 7, + "column": 1 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 70, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 7, + "column": 1 + } + }, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ClassDeclaration", + "start": 0, + "end": 70, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 7, + "column": 1 + } + }, + "id": { + "type": "Identifier", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + }, + "identifierName": "A" + }, + "name": "A" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 8, + "end": 70, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 7, + "column": 1 + } + }, + "body": [ + { + "type": "ClassPrivateProperty", + "start": 12, + "end": 23, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 13 + } + }, + "static": false, + "key": { + "type": "PrivateName", + "start": 12, + "end": 14, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "id": { + "type": "Identifier", + "start": 13, + "end": 14, + "loc": { + "start": { + "line": 2, + "column": 3 + }, + "end": { + "line": 2, + "column": 4 + }, + "identifierName": "a" + }, + "name": "a" + } + }, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 14, + "end": 22, + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 2, + "column": 12 + } + }, + "typeAnnotation": { + "type": "TSStringKeyword", + "start": 16, + "end": 22, + "loc": { + "start": { + "line": 2, + "column": 6 + }, + "end": { + "line": 2, + "column": 12 + } + } + } + }, + "value": null + }, + { + "type": "ClassPrivateProperty", + "start": 26, + "end": 30, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 6 + } + }, + "static": false, + "key": { + "type": "PrivateName", + "start": 26, + "end": 28, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 4 + } + }, + "id": { + "type": "Identifier", + "start": 27, + "end": 28, + "loc": { + "start": { + "line": 3, + "column": 3 + }, + "end": { + "line": 3, + "column": 4 + }, + "identifierName": "b" + }, + "name": "b" + } + }, + "optional": true, + "value": null + }, + { + "type": "ClassPrivateProperty", + "start": 33, + "end": 45, + "loc": { + "start": { + "line": 4, + "column": 2 + }, + "end": { + "line": 4, + "column": 14 + } + }, + "static": false, + "key": { + "type": "PrivateName", + "start": 33, + "end": 35, + "loc": { + "start": { + "line": 4, + "column": 2 + }, + "end": { + "line": 4, + "column": 4 + } + }, + "id": { + "type": "Identifier", + "start": 34, + "end": 35, + "loc": { + "start": { + "line": 4, + "column": 3 + }, + "end": { + "line": 4, + "column": 4 + }, + "identifierName": "c" + }, + "name": "c" + } + }, + "optional": true, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 36, + "end": 44, + "loc": { + "start": { + "line": 4, + "column": 5 + }, + "end": { + "line": 4, + "column": 13 + } + }, + "typeAnnotation": { + "type": "TSNumberKeyword", + "start": 38, + "end": 44, + "loc": { + "start": { + "line": 4, + "column": 7 + }, + "end": { + "line": 4, + "column": 13 + } + } + } + }, + "value": null + }, + { + "type": "ClassPrivateProperty", + "start": 48, + "end": 52, + "loc": { + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 6 + } + }, + "static": false, + "key": { + "type": "PrivateName", + "start": 48, + "end": 50, + "loc": { + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 4 + } + }, + "id": { + "type": "Identifier", + "start": 49, + "end": 50, + "loc": { + "start": { + "line": 5, + "column": 3 + }, + "end": { + "line": 5, + "column": 4 + }, + "identifierName": "d" + }, + "name": "d" + } + }, + "definite": true, + "value": null + }, + { + "type": "ClassPrivateProperty", + "start": 55, + "end": 68, + "loc": { + "start": { + "line": 6, + "column": 2 + }, + "end": { + "line": 6, + "column": 15 + } + }, + "static": false, + "key": { + "type": "PrivateName", + "start": 55, + "end": 57, + "loc": { + "start": { + "line": 6, + "column": 2 + }, + "end": { + "line": 6, + "column": 4 + } + }, + "id": { + "type": "Identifier", + "start": 56, + "end": 57, + "loc": { + "start": { + "line": 6, + "column": 3 + }, + "end": { + "line": 6, + "column": 4 + }, + "identifierName": "e" + }, + "name": "e" + } + }, + "definite": true, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 58, + "end": 67, + "loc": { + "start": { + "line": 6, + "column": 5 + }, + "end": { + "line": 6, + "column": 14 + } + }, + "typeAnnotation": { + "type": "TSBooleanKeyword", + "start": 60, + "end": 67, + "loc": { + "start": { + "line": 6, + "column": 7 + }, + "end": { + "line": 6, + "column": 14 + } + } + } + }, + "value": null + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file