From 6f3be3a5438739453510ea969136894c9cae12d8 Mon Sep 17 00:00:00 2001 From: Andy Date: Sat, 24 Feb 2018 14:26:07 -0800 Subject: [PATCH] typescript: Support definite assignment assertion (#7159) --- .../babel-generator/src/generators/classes.js | 5 +- .../src/generators/statements.js | 1 + .../typescript/class-properties/input.js | 2 + .../typescript/class-properties/output.js | 2 + .../variable-declarator-exclamation/actual.js | 1 + .../expected.js | 1 + .../src/index.js | 6 + .../test/fixtures/class/properties/input.js | 1 + .../test/fixtures/class/properties/output.js | 1 + .../exclamation/actual.js | 1 + .../exclamation/expected.js | 1 + packages/babel-types/src/definitions/core.js | 4 + .../src/definitions/experimental.js | 4 + packages/babylon/src/plugins/typescript.js | 12 +- packages/babylon/src/types.js | 4 + .../typescript/class/properties/input.js | 2 + .../typescript/class/properties/output.json | 118 ++++++++++++++++-- .../input.js | 3 + .../options.json | 3 + .../definite-assignment-not-allowed/input.js | 1 + .../options.json | 3 + .../definite-assignment/input.js | 1 + .../definite-assignment/output.json | 116 +++++++++++++++++ 23 files changed, 283 insertions(+), 10 deletions(-) create mode 100644 packages/babel-generator/test/fixtures/typescript/variable-declarator-exclamation/actual.js create mode 100644 packages/babel-generator/test/fixtures/typescript/variable-declarator-exclamation/expected.js create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/exclamation/actual.js create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/exclamation/expected.js create mode 100644 packages/babylon/test/fixtures/typescript/class/property-optional-definite-assignment-not-allowed/input.js create mode 100644 packages/babylon/test/fixtures/typescript/class/property-optional-definite-assignment-not-allowed/options.json create mode 100644 packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment-not-allowed/input.js create mode 100644 packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment-not-allowed/options.json create mode 100644 packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment/input.js create mode 100644 packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment/output.json diff --git a/packages/babel-generator/src/generators/classes.js b/packages/babel-generator/src/generators/classes.js index fa83be944c..1649cdee39 100644 --- a/packages/babel-generator/src/generators/classes.js +++ b/packages/babel-generator/src/generators/classes.js @@ -99,10 +99,13 @@ export function ClassProperty(node: Object) { this.print(node.key, node); } + // TS if (node.optional) { - // TS this.token("?"); } + if (node.definite) { + this.token("!"); + } this.print(node.typeAnnotation, node); if (node.value) { diff --git a/packages/babel-generator/src/generators/statements.js b/packages/babel-generator/src/generators/statements.js index c3644a4c93..e410092a9e 100644 --- a/packages/babel-generator/src/generators/statements.js +++ b/packages/babel-generator/src/generators/statements.js @@ -290,6 +290,7 @@ export function VariableDeclaration(node: Object, parent: Object) { export function VariableDeclarator(node: Object) { this.print(node.id, node); + if (node.definite) this.token("!"); // TS this.print(node.id.typeAnnotation, node); if (node.init) { this.space(); diff --git a/packages/babel-generator/test/fixtures/typescript/class-properties/input.js b/packages/babel-generator/test/fixtures/typescript/class-properties/input.js index 8c0ad68965..51413533ae 100644 --- a/packages/babel-generator/test/fixtures/typescript/class-properties/input.js +++ b/packages/babel-generator/test/fixtures/typescript/class-properties/input.js @@ -3,4 +3,6 @@ class C { x?; x: number; x: number = 1; + x!; + x!: number; } diff --git a/packages/babel-generator/test/fixtures/typescript/class-properties/output.js b/packages/babel-generator/test/fixtures/typescript/class-properties/output.js index 6ae9081d49..7318dc2f1d 100644 --- a/packages/babel-generator/test/fixtures/typescript/class-properties/output.js +++ b/packages/babel-generator/test/fixtures/typescript/class-properties/output.js @@ -3,4 +3,6 @@ class C { x?; x: number; x: number = 1; + x!; + x!: number; } \ No newline at end of file diff --git a/packages/babel-generator/test/fixtures/typescript/variable-declarator-exclamation/actual.js b/packages/babel-generator/test/fixtures/typescript/variable-declarator-exclamation/actual.js new file mode 100644 index 0000000000..f27135a2b9 --- /dev/null +++ b/packages/babel-generator/test/fixtures/typescript/variable-declarator-exclamation/actual.js @@ -0,0 +1 @@ +let x!: number; diff --git a/packages/babel-generator/test/fixtures/typescript/variable-declarator-exclamation/expected.js b/packages/babel-generator/test/fixtures/typescript/variable-declarator-exclamation/expected.js new file mode 100644 index 0000000000..73cac9cc10 --- /dev/null +++ b/packages/babel-generator/test/fixtures/typescript/variable-declarator-exclamation/expected.js @@ -0,0 +1 @@ +let x!: number; \ No newline at end of file diff --git a/packages/babel-plugin-transform-typescript/src/index.js b/packages/babel-plugin-transform-typescript/src/index.js index efb7ae5e13..986fdf77ab 100644 --- a/packages/babel-plugin-transform-typescript/src/index.js +++ b/packages/babel-plugin-transform-typescript/src/index.js @@ -79,6 +79,10 @@ export default function() { if (path.node.declare) path.remove(); }, + VariableDeclarator({ node }) { + if (node.definite) node.definite = null; + }, + ClassMethod(path) { const { node } = path; @@ -154,7 +158,9 @@ export default function() { if (node.accessibility) node.accessibility = null; if (node.abstract) node.abstract = null; + if (node.readonly) node.readonly = null; if (node.optional) node.optional = null; + if (node.definite) node.definite = null; if (node.typeAnnotation) node.typeAnnotation = null; }, diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/input.js b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/input.js index 36c4f340ca..ce12b25f6f 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/input.js +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/input.js @@ -1,4 +1,5 @@ class C { public a?: number; private b: number = 0; + readonly c!: number = 1; } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/output.js index 14de70ecc5..77e91a62b0 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/output.js +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/output.js @@ -1,3 +1,4 @@ class C { b = 0; + c = 1; } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/exclamation/actual.js b/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/exclamation/actual.js new file mode 100644 index 0000000000..f27135a2b9 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/exclamation/actual.js @@ -0,0 +1 @@ +let x!: number; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/exclamation/expected.js b/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/exclamation/expected.js new file mode 100644 index 0000000000..2756c24c45 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/variable-declaration/exclamation/expected.js @@ -0,0 +1 @@ +let x; diff --git a/packages/babel-types/src/definitions/core.js b/packages/babel-types/src/definitions/core.js index 6845a78a3f..0bd5c2ae33 100644 --- a/packages/babel-types/src/definitions/core.js +++ b/packages/babel-types/src/definitions/core.js @@ -836,6 +836,10 @@ defineType("VariableDeclarator", { id: { validate: assertNodeType("LVal"), }, + definite: { + optional: true, + validate: assertValueType("boolean"), + }, init: { optional: true, validate: assertNodeType("Expression"), diff --git a/packages/babel-types/src/definitions/experimental.js b/packages/babel-types/src/definitions/experimental.js index d10d2c47c0..c9d7b1a301 100644 --- a/packages/babel-types/src/definitions/experimental.js +++ b/packages/babel-types/src/definitions/experimental.js @@ -36,6 +36,10 @@ defineType("ClassProperty", { validate: assertNodeType("Expression"), optional: true, }, + definite: { + validate: assertValueType("boolean"), + optional: true, + }, typeAnnotation: { validate: assertNodeType("TypeAnnotation", "TSTypeAnnotation", "Noop"), optional: true, diff --git a/packages/babylon/src/plugins/typescript.js b/packages/babylon/src/plugins/typescript.js index 69086d5542..5af2b00f0b 100644 --- a/packages/babylon/src/plugins/typescript.js +++ b/packages/babylon/src/plugins/typescript.js @@ -1691,6 +1691,10 @@ export default (superClass: Class): Class => } parseClassProperty(node: N.ClassProperty): N.ClassProperty { + if (!node.optional && this.eat(tt.bang)) { + node.definite = true; + } + const type = this.tsTryParseTypeAnnotation(); if (type) node.typeAnnotation = type; return super.parseClassProperty(node); @@ -1752,6 +1756,10 @@ export default (superClass: Class): Class => // `let x: number;` parseVarHead(decl: N.VariableDeclarator): void { super.parseVarHead(decl); + if (decl.id.type === "Identifier" && this.eat(tt.bang)) { + decl.definite = true; + } + const type = this.tsTryParseTypeAnnotation(); if (type) { decl.id.typeAnnotation = type; @@ -1980,7 +1988,9 @@ export default (superClass: Class): Class => } isClassProperty(): boolean { - return this.match(tt.colon) || super.isClassProperty(); + return ( + this.match(tt.bang) || this.match(tt.colon) || super.isClassProperty() + ); } parseMaybeDefault(...args): N.Pattern { diff --git a/packages/babylon/src/types.js b/packages/babylon/src/types.js index 835f36b6d2..e892b7d159 100644 --- a/packages/babylon/src/types.js +++ b/packages/babylon/src/types.js @@ -326,6 +326,9 @@ export type VariableDeclarator = NodeBase & { type: "VariableDeclarator", id: Pattern, init: ?Expression, + + // TypeScript only: + definite?: true, }; // Misc @@ -696,6 +699,7 @@ export type ClassProperty = ClassMemberBase & { // TypeScript only: (TODO: Not in spec) readonly?: true, + definite?: true, }; export type ClassPrivateProperty = NodeBase & { diff --git a/packages/babylon/test/fixtures/typescript/class/properties/input.js b/packages/babylon/test/fixtures/typescript/class/properties/input.js index 8c0ad68965..51413533ae 100644 --- a/packages/babylon/test/fixtures/typescript/class/properties/input.js +++ b/packages/babylon/test/fixtures/typescript/class/properties/input.js @@ -3,4 +3,6 @@ class C { x?; x: number; x: number = 1; + x!; + x!: number; } diff --git a/packages/babylon/test/fixtures/typescript/class/properties/output.json b/packages/babylon/test/fixtures/typescript/class/properties/output.json index edbf544f70..90e0d7df22 100644 --- a/packages/babylon/test/fixtures/typescript/class/properties/output.json +++ b/packages/babylon/test/fixtures/typescript/class/properties/output.json @@ -1,28 +1,28 @@ { "type": "File", "start": 0, - "end": 60, + "end": 84, "loc": { "start": { "line": 1, "column": 0 }, "end": { - "line": 6, + "line": 8, "column": 1 } }, "program": { "type": "Program", "start": 0, - "end": 60, + "end": 84, "loc": { "start": { "line": 1, "column": 0 }, "end": { - "line": 6, + "line": 8, "column": 1 } }, @@ -31,14 +31,14 @@ { "type": "ClassDeclaration", "start": 0, - "end": 60, + "end": 84, "loc": { "start": { "line": 1, "column": 0 }, "end": { - "line": 6, + "line": 8, "column": 1 } }, @@ -63,14 +63,14 @@ "body": { "type": "ClassBody", "start": 8, - "end": 60, + "end": 84, "loc": { "start": { "line": 1, "column": 8 }, "end": { - "line": 6, + "line": 8, "column": 1 } }, @@ -294,6 +294,108 @@ }, "value": 1 } + }, + { + "type": "ClassProperty", + "start": 63, + "end": 66, + "loc": { + "start": { + "line": 6, + "column": 4 + }, + "end": { + "line": 6, + "column": 7 + } + }, + "static": false, + "key": { + "type": "Identifier", + "start": 63, + "end": 64, + "loc": { + "start": { + "line": 6, + "column": 4 + }, + "end": { + "line": 6, + "column": 5 + }, + "identifierName": "x" + }, + "name": "x" + }, + "computed": false, + "definite": true, + "value": null + }, + { + "type": "ClassProperty", + "start": 71, + "end": 82, + "loc": { + "start": { + "line": 7, + "column": 4 + }, + "end": { + "line": 7, + "column": 15 + } + }, + "static": false, + "key": { + "type": "Identifier", + "start": 71, + "end": 72, + "loc": { + "start": { + "line": 7, + "column": 4 + }, + "end": { + "line": 7, + "column": 5 + }, + "identifierName": "x" + }, + "name": "x" + }, + "computed": false, + "definite": true, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 73, + "end": 81, + "loc": { + "start": { + "line": 7, + "column": 6 + }, + "end": { + "line": 7, + "column": 14 + } + }, + "typeAnnotation": { + "type": "TSNumberKeyword", + "start": 75, + "end": 81, + "loc": { + "start": { + "line": 7, + "column": 8 + }, + "end": { + "line": 7, + "column": 14 + } + } + } + }, + "value": null } ] } diff --git a/packages/babylon/test/fixtures/typescript/class/property-optional-definite-assignment-not-allowed/input.js b/packages/babylon/test/fixtures/typescript/class/property-optional-definite-assignment-not-allowed/input.js new file mode 100644 index 0000000000..f649b70d9c --- /dev/null +++ b/packages/babylon/test/fixtures/typescript/class/property-optional-definite-assignment-not-allowed/input.js @@ -0,0 +1,3 @@ +class C { + x?!: number; +} diff --git a/packages/babylon/test/fixtures/typescript/class/property-optional-definite-assignment-not-allowed/options.json b/packages/babylon/test/fixtures/typescript/class/property-optional-definite-assignment-not-allowed/options.json new file mode 100644 index 0000000000..626e94c744 --- /dev/null +++ b/packages/babylon/test/fixtures/typescript/class/property-optional-definite-assignment-not-allowed/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Unexpected token, expected \";\" (2:4)" +} \ No newline at end of file diff --git a/packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment-not-allowed/input.js b/packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment-not-allowed/input.js new file mode 100644 index 0000000000..e91abc1a9d --- /dev/null +++ b/packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment-not-allowed/input.js @@ -0,0 +1 @@ +let {}! = {}; \ No newline at end of file diff --git a/packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment-not-allowed/options.json b/packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment-not-allowed/options.json new file mode 100644 index 0000000000..8cb567ee77 --- /dev/null +++ b/packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment-not-allowed/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Complex binding patterns require an initialization value (1:6)" +} \ No newline at end of file diff --git a/packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment/input.js b/packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment/input.js new file mode 100644 index 0000000000..f27135a2b9 --- /dev/null +++ b/packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment/input.js @@ -0,0 +1 @@ +let x!: number; diff --git a/packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment/output.json b/packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment/output.json new file mode 100644 index 0000000000..d47c7ce44b --- /dev/null +++ b/packages/babylon/test/fixtures/typescript/variable-declarator/definite-assignment/output.json @@ -0,0 +1,116 @@ +{ + "type": "File", + "start": 0, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 15 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 15 + } + }, + "sourceType": "module", + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 15 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 4, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "id": { + "type": "Identifier", + "start": 4, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 4 + }, + "end": { + "line": 1, + "column": 14 + }, + "identifierName": "x" + }, + "name": "x", + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 6, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "typeAnnotation": { + "type": "TSNumberKeyword", + "start": 8, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 14 + } + } + } + } + }, + "definite": true, + "init": null + } + ], + "kind": "let" + } + ], + "directives": [] + } +} \ No newline at end of file