Skip to content

Commit

Permalink
implement declared class fields
Browse files Browse the repository at this point in the history
Summary:
Flow implemented class fields while they were very early-stage, behind the `esproposal.class_instance_fields` and `esproposal.class_static_fields` flowconfig options.

In current versions of the proposal, fields without initializers now default to `undefined`: [[design](https://github.com/tc39/proposal-class-fields#fields-without-initializers-are-set-to-undefined)] [[spec](https://tc39.es/proposal-class-fields/#sec-define-field)]

Babel < 8 stripped uninitialized fields, which is not spec-compliant. Babel 8 changes to leaving the field and just removing the annotation, as you'd rightly expect, but making it impossible* to have type-only fields. [[babel issue](babel/babel#10039)] [[babel 8 notes](babel/babel#10746)]

This diff introduces a `declare` syntax to provide a solution for type-only fields:

```
class C {
  declare foo: string;
  bar: string;
}

// becomes
class C {
  bar;
}
```

As far as Flow is concerned, both `foo` and `bar` are treated as before, as if they are `string`:

```
const x = new C;
(x.foo: string);
(x.bar: string);
```

Fixes #6811

[* technically you can use Flow comment syntax -- `class C { /*:: prop: string; */ }` but we don't want to require that]

Reviewed By: samwgoldman

Differential Revision: D20088452

fbshipit-source-id: f00299539c9387a55102e9a4e554a3b3bcb3498e
  • Loading branch information
mroch authored and facebook-github-bot committed Feb 27, 2020
1 parent e68dd26 commit 11b7adb
Show file tree
Hide file tree
Showing 33 changed files with 765 additions and 52 deletions.
14 changes: 13 additions & 1 deletion packages/flow-remove-types/flow-remove-types
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ var usage = 'Usage: flow-remove-types [options] [sources] \n' +
' -a, --all Transform all files, not just those with a @flow comment\n' +
' -p, --pretty Remove flow types without replacing with spaces, \n' +
' producing prettier output but may require using source maps\n' +
' --ignore-uninitialized-fields\n' +
' Removes uninitialized class fields (`foo;`, `foo: string;`)\n' +
' completely rather than only removing the type. THIS IS NOT\n' +
' SPEC COMPLIANT! Instead, use `declare foo: string;` for\n' +
' type-only fields.\n' +
' -m, --sourcemaps Also output source map files. Optionally pass "inline"\n' +
' -q, --quiet Does not produce any output concerning successful progress.\n' +

Expand Down Expand Up @@ -79,6 +84,7 @@ var outDir;
var outFile;
var all;
var pretty;
var ignoreUninitializedFields;
var sourceMaps;
var inlineSourceMaps;
var quiet;
Expand All @@ -104,6 +110,8 @@ while (i < process.argv.length) {
all = true;
} else if (arg === '-p' || arg === '--pretty') {
pretty = true;
} else if (arg === '--ignore-uninitialized-fields') {
ignoreUninitializedFields = true;
} else if (arg === '-m' || arg === '--sourcemaps') {
sourceMaps = true;
if (process.argv[i] === 'inline') {
Expand Down Expand Up @@ -242,7 +250,11 @@ function btoa(str) {

function transformSource(content, filepath) {
try {
return flowRemoveTypes(content, { all: all, pretty: pretty });
return flowRemoveTypes(content, {
all: all,
pretty: pretty,
ignoreUninitializedFields: ignoreUninitializedFields,
});
} catch (error) {
if (error.loc) {
var line = error.loc.line - 1;
Expand Down
18 changes: 14 additions & 4 deletions packages/flow-remove-types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/

var parse = require('flow-parser').parse;
Expand All @@ -21,6 +23,11 @@ var vlq = require('vlq');
* If true, removes types completely rather than replacing with spaces.
* This may require using source maps.
*
* - ignoreUninitializedFields: (default: false)
* If true, removes uninitialized class fields (`foo;`, `foo: string;`)
* completely rather than only removing the type. THIS IS NOT SPEC
* COMPLIANT! Instead, use `declare foo: string;` for type-only fields.
*
* Returns an object with two methods:
*
* - .toString()
Expand All @@ -34,7 +41,7 @@ module.exports = function flowRemoveTypes(source, options) {
var all = Boolean(options && options.all);
if (options && options.checkPragma) {
throw new Error(
'flow-remove-types: the "checkPragma" option has been replaced by "all".'
'flow-remove-types: the "checkPragma" option has been replaced by "all".',
);
}

Expand Down Expand Up @@ -68,6 +75,9 @@ module.exports = function flowRemoveTypes(source, options) {
source: source,
removedNodes: removedNodes,
pretty: Boolean(options && options.pretty),
ignoreUninitializedFields: Boolean(
options && options.ignoreUninitializedFields,
),
};

// Remove the flow pragma.
Expand Down Expand Up @@ -172,7 +182,7 @@ var removeFlowVisitor = {
},

ClassProperty: function(context, node) {
if (!node.value) {
if (node.declare || (context.ignoreUninitializedFields && !node.value)) {
return removeNode(context, node);
}
},
Expand Down Expand Up @@ -256,8 +266,8 @@ var removeFlowVisitor = {
context,
endOf(ast.tokens[paramEndIdx]),
ast.tokens[paramEndIdx].loc.end,
' =>'
)
' =>',
),
);

// Delete the original arrow token.
Expand Down
3 changes: 3 additions & 0 deletions packages/flow-remove-types/test-update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
# Generate expected output
./flow-remove-types test/source.js > test/expected.js;

# Generate expected output with --ignore-uninitialized-fields flag
./flow-remove-types --ignore-uninitialized-fields test/source.js > test/expected-uninitialized-fields.js;

# Generate expected output with --pretty flag
./flow-remove-types --pretty test/source.js > test/expected-pretty.js;

Expand Down
5 changes: 5 additions & 0 deletions packages/flow-remove-types/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ echo "Test: flow-remove-types test/source.js"
DIFF=$(./flow-remove-types test/source.js | diff test/expected.js -);
if [ -n "$DIFF" ]; then echo "$DIFF"; exit 1; fi;

# Test expected output with --ignore-uninitialized-fields flag
echo "Test: flow-remove-types --ignore-uninitialized-fields test/source.js"
DIFF=$(./flow-remove-types --ignore-uninitialized-fields test/source.js | diff test/expected-uninitialized-fields.js -);
if [ -n "$DIFF" ]; then echo "$DIFF"; exit 1; fi;

# Test expected output with --pretty flag
echo "Test: flow-remove-types --pretty test/source.js"
DIFF=$(./flow-remove-types --pretty test/source.js | diff test/expected-pretty.js -);
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 11b7adb

Please sign in to comment.