TypeScriptのコンパイラ・プラグインとして振る舞いASTの操作を行えるcustom transformer (AST preprocessor) が実装されたのは TypeScript 2.4 (2017年) でした。
そのときの様子は次のエントリに非常によくまとまっています。
[TypeScript 2.4] custom transformer を利用して実行時に型情報を参照可能にする - Qiita
さて、現在のTypeScript v3.3 でのTransformer APIの状況はというと、TypeScript compiler本体としては依然としてドキュメントに乗ってない水準の扱いです。特に、コンパイラAPI そのままではtransformerは型情報を利用できません。ts.TransformerFactory
に渡される ts.TransformationContext
に ts.Program
や ts.TypeChecker
が存在しないためです。これは、TypeScriptコンパイラ開発チームによれば意図的な設計ということです。
(あると便利だから再検討してほしい!とコメントはしましたが、望みは薄いでしょう)
つまり、標準的なtransformerができることは、babel pluginとできることに本質的な違いがない、ということで、これではあまり面白くないですね。
しかし先に紹介したエントリではコンパイル時型情報を実行時型情報(=JavaScriptのオブジェクト)に変化しています。コンパイラAPIを駆使すればtransformerが型情報も扱えるということですね。しかしそれはtransformer用にカスタムコンパイラを作ることに等しいため、transformerは開発するのもテストするのも利用するのも難易度が高い状態です。
とはいえ、2019年現在では、有志によって開発されたプラガブルなカスタムコンパイラでtransformerの開発・利用が少しだけ楽になりました。
- ts-loader
- getCustomTransformers はtransformerに
ts.Program
を与えます ts.Program#getTypeChecker()
でts.TypeChecker
にアクセスできるので、これを通じて型情報を利用できます
- getCustomTransformers はtransformerに
- ttypescript (TypeScript with Transformers)
- TypeScript compilerのかわりに利用できるカスタムコンパイラで、tsconfig.jsonにtransformerを設定できます
- 設定できるtransformerは
ts.Program
やts.TypeChecker
を受けとるようにできます
- 設定できるtransformerは
ts-node
は--typescript
オプションでカスタムコンパイラを設定できるため、ts-node --typescript=ttypescript
とすると ts-node でも型情報を利用するtransformerを利用できますts-loader
もtypescript
オプションでttypescript
を利用できます
- TypeScript compilerのかわりに利用できるカスタムコンパイラで、tsconfig.jsonにtransformerを設定できます
このあたりは先のエントリの作者による ts-transform-keys のREADMEに詳しく載っています。
https://github.com/kimamula/ts-transformer-keys
とはいえ、有志によるカスタムコンパイラに依存しすぎるのもあまり望ましくないので、できれば標準のコンパイラAPIとして型情報へのアクセス方法を解放してほしいところです。
Transformerが型情報にアクセスできると何ができるか
ぱっと考えただけですが、次のようなことができます。
まず、TypeScriptの型情報を React の prop types に変換できるはずです。通常であればTypeScriptの型情報はコンパイル時に失われるので次のような typeToPropTypes<T>()
は実装しようがないのですが、transformerがこの関数呼び出しをprop typesに置き換えてしまえばいいというわけです(まったく試していませんが、このアイデアを実装したtransformerはすでにあるようです https://github.com/joelday/ts-proptypes-transformer )。
// これはイメージです interface Props { /* ... */ } cass SomeComponent extends React.Component<Props> { static redonly propTypes = typeToPropTypes<Props>(); }
次に、TypeScriptの型情報をGraphQLのクエリ(クエリフラグメント)に変換できるのではないかと考えています。
// これはイメージです interface Tweet { author: { id }; content: string; tweetedAt: Date; } const GetTweet = gql` query { tweets { edges { // { author { id }; content; tweetedAt } に変換する ...${typeToQueryFragment<Tweet>()} } } } `;
というわけで、TypeScriptの型情報を利用したtransformerを安定して開発・利用できるようになるといろいろ夢がありますね。ただし、繰り返しになりますが、現在はその利用のためにTypeScriptのカスタムコンパイラを作る必要があります。プロダクションに入れる際は自己責任でどうぞ。