2016年9月現在だとReactアプリのTypeScript採用は時期尚早(※追記あり)

追記(2019/04/16): 2017年半ばにここで触れているプロジェクトはTSに移行しました。今となってはTS+Reactの組み合わせは全く問題がなく、むしろ非常に相性のよい組み合わせであるとすらいえます。

TypeScript化の調査 2016年9月現在(React v15.3.1, TypeScript 2.0-rc)の話です。

いま開発しているウェブアプリのフロントエンドをTypeScript化しようと思ってちょっと調べてみたんですが、今やるのはいくらTypeScript推進派でもちょっと厳しいなと。

  • TypeScriptでimportできるライブラリは、TypeScriptのコード(.ts)またはdts: TypeScript definition files (.d.ts) のみ
  • Reactは素のJS + 一部FlowType なのでdtsの公式提供は期待できない
  • DefinitelyTypedのdtsは手動でメンテナンスされているよう
    • https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/react
    • これが現在 v0.14 ベースで古い
    • v15.3ベースに修正するにしても作業量が多すぎてつらい
    • 仮に今がんばってv15.3ベースにしたとしても将来にわたって最新版に追従し続けるのがつらい
    • つまりこの規模の大型ライブラリのdtsを手作業でメンテナンスし続けるのはつらい

将来的には、 ReactコードのほぼすべてにFlowTypeアノテーションがついてdtsがそこから自動生成されるというようにはできるだろうと思っているのですが、現状そうではないので、繰り返しになりますが現時点での導入はかなり厳しいと思います。

次のように、v15.3のランタイムとv0.14のdtsを組み合わせてなんとなく動かすことはできるんですが、たぶん茨の道だろうと思いますね…:


追記:

--allowJs でとりあえず導入みたいなのはありかもしれないですね。TypeScriptの恩恵を受けられないので素のJSよりどのくらい楽になるかはちょっとやってみないとわかりませんが。

dts-gen(1) でJavaScripファイルからdtsを生成する

2016-09-12のJS: Polymer 2.0 Preview、Service Worker入門、JSから型定義ファイルの生成 - JSer.infogen-flow-filesdts-gen を知ったのでちょっとだけ使ってみました。

gen-flow-files (as of flow v0.32.0)

Release v0.32.0 · facebook/flow · GitHub

We're starting work on flow gen-flow-files, which consumes Flow code and outputs .flow files containing only the types exported. It's alpha-level and we're still iterating on it, so use at your own peril!

flow(1)のサブコマンドで、flow type annotation付きのJSファイルから型定義ファイルを抽出するものです。処理対象のコードは、すべて型アノテーション付きでなければいけません。

ふつうのJSファイルには使えないので、何に使うのかよくわかりませんでした。npm packageを作る際、pure JSと型定義ファイルに分割する用でしょうか。TypeScriptはそうしているので。

dts-gen (as of v0.4.16)

GitHub - Microsoft/dts-gen: dts-gen creates starter TypeScript definition files for any module or library.

こちらは独立したコマンドで、素のJSから TypeScript definition files (dts) を生成するコマンドです。

いまのところ入力はglobalにインストールされたモジュールかロードされたシンボルのみなので使い勝手がイマイチですが、説明にある通り手書きでdtsを書く際のスターターとして使い、そのままだとanyが多いので型を書き足していくような使い方を想定していそうです。

たとえばnodejs標準ライブラリのchild_processはこんな感じです。

dts-gen -s -m child_process
export class ChildProcess {
    constructor();
    kill(sig: any): any;
    ref(): void;
    spawn(options: any): any;
    unref(): void;
}
export function exec(command: any, ...args: any[]): any;
export function execFile(file: any, ...args: any[]): any;
export function execFileSync(...args: any[]): any;
export function execSync(command: any, ...args: any[]): any;
export function fork(modulePath: any, ...args: any[]): any;
export function spawn(...args: any[]): any;
export function spawnSync(...args: any[]): any;

class ChildProcess のメソッドはそこそこ揃ってますが、プロパティは全滅ですね。 ChildProcess#disconnect() など見当たらないメソッドもあります。

コードを読んでみると、入力を純粋にJSのオブジェクトとして評価してそこから情報を取り出しているだけの非常に単純なものなのでした。将来的にはフロー解析して型推論してくれるとよさそうです。なお、tscの --allowJs との差などは調べてません。