LoginSignup
11
12

More than 5 years have passed since last update.

flowtypeのdeclareとimportの話

Last updated at Posted at 2015-05-25

flowtypeの導入を断念しました。とはいえ数週間ほどやって知見が溜まったので、それをメモがてら残しておきます。

前提

前提としてFlow Commentsで使用しています。

これは良いか悪いかさておき、実装としてのコードと、型を補完するコードを分けることができるので、融通が利くというのでFlow Commentsを使用しています。つまりはこういうことです。

var awslambda/*: Lambda */ = new AWS.Lambda();
var lambda = Bluebird.promisifyAll(lambda, {suffix: 'Promise'});
/*::
lambda = {};
lambda.addPermissionPromise = Bluebird.promisify(awslambda.addPermission);
lambda.createEventSourceMappingPromise = Bluebird.promisify(awslambda.createEventSourceMapping);
lambda.createFunctionPromise = Bluebird.promisify(awslambda.createFunction);
lambda.deleteEventSourceMappingPromise = Bluebird.promisify(awslambda.deleteEventSourceMapping);
lambda.deleteFunctionPromise = Bluebird.promisify(awslambda.deleteFunction);
lambda.getEventSourceMappingPromise = Bluebird.promisify(awslambda.getEventSourceMapping);
lambda.getFunctionPromise = Bluebird.promisify(awslambda.getFunction);
lambda.getFunctionConfigurationPromise = Bluebird.promisify(awslambda.getFunctionConfiguration);
lambda.getPolicyPromise = Bluebird.promisify(awslambda.getPolicy);
lambda.invokePromise = Bluebird.promisify(awslambda.invoke);
lambda.invokeAsyncPromise = Bluebird.promisify(awslambda.invokeAsync);
lambda.listEventSourceMappingsPromise = Bluebird.promisify(awslambda.listEventSourceMappings);
lambda.listFunctionsPromise = Bluebird.promisify(awslambda.listFunctions);
lambda.removePermissionPromise = Bluebird.promisify(awslambda.removePermission);
lambda.updateEventSourceMappingPromise = Bluebird.promisify(awslambda.updateEventSourceMapping);
lambda.updateFunctionCodePromise = Bluebird.promisify(awslambda.updateFunctionCode);
lambda.updateFunctionConfigurationPromise = Bluebird.promisify(awslambda.updateFunctionConfiguration);
*/

(ツラいですが!ツラいですが!!!

まぁ、型のためのコードをコメントとして残せるので、ツラいことに目をつぶればどうにでも出来るというのはメリットです。

外部宣言ファイルの読み込み

declareで行う宣言は外部ファイルで定義して、checkするときにそのファイルを読み込むということができます。

外部宣言したファイルを読み込む方法として2つあり、flow check --lib [path]で指定する方法と、.flowconfig[libs]を定義する2つの方法があります。
どちらを使うかは好き好きかなと思います。

declare

flowtypeではDeclarationsとして型を宣言する機能があります。これとImportを組み合わせることで3rdパーティのモジュールを型定義して読み込むことができます。

declare module

存在しないモジュールを読み込む構文としてdeclare moduleがあります。

declare class C {
  x: string;
}
declare module M {
  declare function foo(c: C): void;
}

declare moduleで宣言したあとに

var M = require('M');
M.foo(new C());

存在しないモジュールをrequire、またはimportを行うとdeclare moduleで宣言したモジュールを受けとることができます。
注意点として、declare module構文はモジュールを定義するための構文ではなく、require(またはimport)できないモジュールを定義するものになるということを気をつけてください。自分はモジュールを定義するための構文だと思ってハマりました。

使い所としては結構難しく、思いつくものとしては

  • 開発中のモジュールのインターフェースを読み込む

ぐらいしか思いつきません。それ以外に使い道が全く思いつかないので、こういうときに使うというのがあれば教えてください。

declare var, function, class

declare module以外の宣言の場合は

import /*:: type */ Package from 'Package';

のようにimport type構文を使って、モジュールを読み込み、それに対してdeclareで宣言した型を付与します。(単純に型を読み込むだけなら外部宣言ファイルの読み込みを行えば使えるようになります)

ただし、aws-sdkのようにクラス内クラスはflowtypeでは宣言できない(たぶん)ため、少し手間をかける必要があります。

interface/lambda.js
declare class Lambda {
  addPermission(request: AddPermissionRequest, callback: (err: any, response: AddPermissionResponse) => void): void;
  createEventSourceMapping(request: CreateEventSourceMappingRequest, callback: (err: any, response: EventSourceMappingConfiguration) => void): void;
  createFunction(request: CreateFunctionRequest, callback: (err: any, response: FunctionConfiguration) => void): void;
  deleteEventSourceMapping(request: DeleteEventSourceMappingRequest, callback: (err: any, response: EventSourceMappingConfiguration) => void): void;
  deleteFunction(request: DeleteFunctionRequest, callback: (err: any, response: void) => void): void;
  getEventSourceMapping(request: GetEventSourceMappingRequest, callback: (err: any, response: EventSourceMappingConfiguration) => void): void;
  getFunction(request: GetFunctionRequest, callback: (err: any, response: GetFunctionResponse) => void): void;
  getFunctionConfiguration(request: GetFunctionConfigurationRequest, callback: (err: any, response: FunctionConfiguration) => void): void;
  getPolicy(request: GetPolicyRequest, callback: (err: any, response: GetPolicyResponse) => void): void;
  invoke(request: InvocationRequest, callback: (err: any, response: InvocationResponse) => void): void;
  invokeAsync(request: InvokeAsyncRequest, callback: (err: any, response: InvokeAsyncResponse) => void): void;
  listEventSourceMappings(request: ListEventSourceMappingsRequest, callback: (err: any, response: ListEventSourceMappingsResponse) => void): void;
  listFunctions(request: ListFunctionsRequest, callback: (err: any, response: ListFunctionsResponse) => void): void;
  removePermission(request: RemovePermissionRequest, callback: (err: any, response: void) => void): void;
  updateEventSourceMapping(request: UpdateEventSourceMappingRequest, callback: (err: any, response: EventSourceMappingConfiguration) => void): void;
  updateFunctionCode(request: UpdateFunctionCodeRequest, callback: (err: any, response: FunctionConfiguration) => void): void;
  updateFunctionConfiguration(request: UpdateFunctionConfigurationRequest, callback: (err: any, response: FunctionConfiguration) => void): void;
}

上記のようにAWSオブジェクト内部のクラスを単独で宣言し

import AWS from 'aws-sdk';
var lambda/*: Lambda */ = new AWS.Lambda();

AWSオブジェクトを通常のimportで読み込んだあとに変数に型定義を付与してあげます。
理想を言えば

declare class AWS {
  static Lambda: Lambda;
}

みたいに宣言をできたら良かったのですが。。。
というか、もっと言えば、d.tsと同じ構文で宣言したかったです。

おわりに

取り敢えず、たかだが宣言とインポートをやりたかっただけなのに、ここまで理解するのに一体何個の地雷を踏んだんだろうというぐらいツラい道のりでした。
ES6+flowtype構成とか実用するには、まだ早すぎました・・・。

11
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
12