diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 5d6922d329eb..8a027f3cafb6 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -1863,7 +1863,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; @@ -2002,16 +2002,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 2c5992d2ba6c..3b530029c9ae 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