実践AST
Flowから
TypeScriptへの変換

We Are JavaScripters! @25th Leko

About me

Node.js, React, React Native, React Native for web

FlowからTypeScript(TS)に移行中

  • 主要な移行対象: 12リポジトリ
  • 中身はmonorepoなので実質5-60パッケージ

→ AST変換でいけないかな

話したいこと

  • AST変換を活用したコード変換の可能性
  • コストetcでTS化をやれてない方の契機に

話さないこと

  • Flowとは何か、TypeScriptとは何か
  • TS化に至った背景や意思決定の話
  • FlowとTSの機能比較や型システムの話
  • Babelプラグインの作り方

Flow → TypeScript

  • 思想は違うけど、結果書く型定義はけっこう似てる
  • 複雑な型を使うと差異が大きくなる

例えば

Flow → TypeScript変換の大枠

  1. Flowで書かれたコードをASTにバラしてTSに変換
  2. ビルド周りの整備(tsconfig, @types etc)
  3. tscで型チェック通す
  4. .d.tsを吐く

Flow → TypeScript with Babel

src/以下のFlowで書かれたjsを、tssrc/に出力

babel \
  --no-babelrc \
  --plugins @babel/plugin-syntax-class-properties \
  --plugins @babel/plugin-syntax-object-rest-spread \
  --plugins @babel/plugin-syntax-jsx \
  --plugins @babel/plugin-syntax-flow \
  --plugins {{TS変換用のBabelプラグイン}} \
  src --out-dir tssrc

syntax... = 解釈だけ、変換しない

class Hoge {
    static staticProperty = "babelIsCool";
}
 
// @babel/plugin-syntax-class-properties
class Hoge {
    static staticProperty = "babelIsCool";
}
 
// @babel/plugin-transform-class-properties
class Hoge {}
Hoge.staticProperty = "babelIsCool";
babel \
  --no-babelrc \
  --plugins @babel/plugin-syntax-class-properties \
  --plugins @babel/plugin-syntax-object-rest-spread \
  --plugins @babel/plugin-syntax-jsx \
  --plugins @babel/plugin-syntax-flow \
  --plugins {{TS変換用のBabelプラグイン}} \
  src --out-dir tssrc
  • Kiikurage/babel-plugin-flow-to-typescript
  • bcherny/flow-to-typescript

複雑な型にも対応してそう!!

すでにあった!勝った!

そんな訳ない

  • どちらも未対応の構文がある
  • 全ての構文をもれなくマッピングするのは無理筋

先述のBabelプラグインのAST変換を
自分の用途に耐える変換処理を書き足す

AST

Abstract Syntax Tree

AST=抽象構文木

  • 構文を木構造にしたもの
  • 構文ごとにtypeがある
  • VariableDeclaration
    = 変数定義
  • NumericLiteral
    = 数値リテラル etc
  • 欲しいtypeを指定して変換

ex. BigInt

  • BinaryExpression
    = 二項演算
  • CallExpression
    = 関数呼び出し

ex. * asを追加したい

// Flow
import React from 'react'

// ↓

// TS
import * as React from 'react'

ex. * asを追加

さまざまなtypeがある。
プロパティ値変えたり構造を変更し構文が変化

type一覧は@babel/typesのドキュメント ※

※ASTパーサにBabel以外を使う場合は別

AST Explorer

小さくはじめて何度もデバッグ

https://astexplorer.net/

AST書き換えた後どうやってtsになるの

ASTを実コードに変換する処理がある

ダメなら、試して、書き換える

無いものは足す

マージされるまで一旦forkでしのぐ

React周りの型名が食い違っている

React.Node → React.ReactNode etc... 😭
これもBabelプラグイン書いて変換

https://github.com/Leko/zapshot/pull/58/files

まとめ

  • 困難を苦労ではなく腕力で殴り倒す💪
  • AST活用してts変換を自動化できた
  • ベースとなる実装があれば、できると思う
    - ゼロベースで作るのはかなり大変

forkはこちらにあります

その他ASTを活用したツール例

  • prettier (コードフォーマッタ)
  • Textlint (自然言語linter)
  • jest-codemods (各テストランナー → Jest)
  • jscodeshift (汎用的なコード変換ツール)

おわり