Subscribed unsubscribe Subscribe Subscribe

Islands in the byte stream

Technical notes by a software engineer

Flowtypeの`flow suggest`がすごい!

flow(1)のコマンドラインオプションを眺めていたら、 suggest というサブコマンドがありました*1

これは、JavaScriptファイルを引数にとって、そのスクリプトにflowtype annotationを付けるパッチを生成するコマンドのようです。

たとえばこういうスクリプトに対して:

function add(a, b) {
    return a + b;
}

console.log(add(1, 2)));

次のようなパッチを生成しました:

--- old
+++ new
@@ -1,4 +1,4 @@
-function add(a, b) {
+function add(a: number, b: number) : number{
     return a + b;
 }

これは結構すごいんじゃないでしょうか。

Reactの1ファイルにも試してみました。

flow suggest src/isomorphic/modern/class/ReactComponent.js で以下のような出力です。

--- old
+++ new
@@ -13,7 +13,7 @@
 
 var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue');
 
-var canDefineProperty = require('canDefineProperty');
+var canDefineProperty: boolean = require('canDefineProperty');
 var emptyObject = require('emptyObject');
 var invariant = require('invariant');
 var warning = require('warning');
@@ -21,7 +21,7 @@
 /**
  * Base class helpers for the updating state of a component.
  */
-function ReactComponent(props, context, updater) {
+function ReactComponent(props, context, updater) : void{
   this.props = props;
   this.context = context;
   this.refs = emptyObject;
@@ -57,7 +57,7 @@
  * @final
  * @protected
  */
-ReactComponent.prototype.setState = function(partialState, callback) {
+ReactComponent.prototype.setState = function(partialState, callback) : void{
   invariant(
     typeof partialState === 'object' ||
     typeof partialState === 'function' ||
@@ -85,7 +85,7 @@
  * @final
  * @protected
  */
-ReactComponent.prototype.forceUpdate = function(callback) {
+ReactComponent.prototype.forceUpdate = function(callback) : void{
   this.updater.enqueueForceUpdate(this);
   if (callback) {
     this.updater.enqueueCallback(this, callback, 'forceUpdate');
@@ -98,7 +98,7 @@
  * modern base class. Instead, we define a getter that warns if it's accessed.
  */
 if (__DEV__) {
-  var deprecatedAPIs = {
+  var deprecatedAPIs: {isMounted: [string, string], replaceState: [string, string]} = {
     isMounted: [
       'isMounted',
       'Instead, make sure to clean up subscriptions and pending requests in ' +
@@ -110,10 +110,10 @@
       'https://github.com/facebook/react/issues/3236).',
     ],
   };
-  var defineDeprecationWarning = function(methodName, info) {
+  var defineDeprecationWarning = function(methodName: string, info) : void{
     if (canDefineProperty) {
       Object.defineProperty(ReactComponent.prototype, methodName, {
-        get: function() {
+        get: function() : void{
           warning(
             false,
             '%s(...) is deprecated in plain JavaScript React classes. %s',
@@ -125,7 +125,7 @@
       });
     }
   };
-  for (var fnName in deprecatedAPIs) {
+  for (var fnName: string in deprecatedAPIs) {
     if (deprecatedAPIs.hasOwnProperty(fnName)) {
       defineDeprecationWarning(fnName, deprecatedAPIs[fnName]);
     }

推論しきれてない部分もありますが、けっこうできているなという印象です。逆にいえば、これで補完されない部分は推論できないということなので、そういうところだけ人が書けばいいということですね。

*1:試したのはflow 0.35.0です