Typescriptをいろいろ試してみた

ゴールデンウィークを利用して、Typescriptをいろいろ試してみました。
JavaScriptでWebアプリケーションを構築する際のベースとして使っている、Nodeyardと同じような感じで使える、Typeyardを作ってみました。

以下の方針で作っています。

  • クライアントサイド、サーバサイド共にTypescriptで記述する
  • クライアントサイドはWebpackを使う
  • スクランナーを利用せず、npm run scriptでビルドする

テストやソースチェック、devサーバなどひとまず自分が使いたい機能は用意することができました。
その中で気付いたことなどを、雑多になりますが書いていきます。

tscが少し使いづらい

Typescriptのコンパイラであるtscの設定が、少し柔軟性に欠ける。

tsconfig.jsonコンパイル対象のファイルをfilesで、除外するファイルをexcludeで指定するのですが、この2つを組み合わせて使うことができません。また、globなどを使ってパターンで指定することができず、ファイル名かディレクトリ名を直接指定することしかできません。

サーバサイドのコードは、ファイルごとにコンパイルして特定のディレクトリに出力したいのですが、クライアントサイドのコードはWebpackを使ってビルドするため、コンパイル対象に含めたくはありません。
excludeに、node_modulesなどと一緒にクライアントサイドのコードのディレクトリを指定すればコンパイルは問題ないのですが、atom-typescriptがtsconfig.jsonを見て動いているようで、クライアントサイドのコードでコード保管等が動いてくれなくなります。

今回は、tsconfig.jsonatom-typescriptとWebpackが参照するから定義はするけれども、実際にサーバサイドのコードをコンパイルする際は、cliでパラメータを渡すようにしました。
サーバサイド用のconfigファイルを定義してそれを指定する…ということができればよかったのですがそれもできない模様。
/src/app.tsというエントリーファイルを用意して、依存するファイルのみコンパイルされるというような形にしています。

Webpackが便利

今回初めてWebpackを使ってみたのですが、いい感じかも。
アプリケーションが利用するスタイルシートや画像などのリソースも一括して管理できるので、クライアントサイドのビルドタスクをいろいろ定義せず、1つで済ますことができるのは非常に便利。
file-loaderで画像をディレクトリ構造や名前を維持したまま、特定のディレクトリに出力することができるので、expressのテンプレートエンジンから利用するのも問題ない感じ。
全体的な考え方は、Reactととても相性が良いと思います。

Express + Typescript

typingsでexpress、express-serve-static-core、serve-static、node、mimeを--save --ambientオプションでまずはインストール。あとは利用するミドルウェアの型定義を適宜インストール。
今回試した範囲では、特に問題は発生せず。
Expressのオブジェクトを利用する関数を定義するときなどは、型を確認して明示的に指定しないといけないけれど、Expressのオブジェクトやメソッドを利用する場合は、型推論が賢くてほぼES6と同様の書き方で大丈夫っぽい。
エラー生成でhttp-errorsを試してみたけど、最終的なエラーハンドリングを行う箇所で、

app.use((err: Error, req, res, next) => {
  const error = err as HttpErrors.HttpError;

  res.status(error.status || 500);

  const params = {
    error: (process.env.NODE_ENV !== "production") ? error : null,
    message: error.message
  };

  if (/^\/api/.test(req.originalUrl)) {
    res.send(params);
  } else {
    res.render("error", params);
  }
});

のようにダウンキャストすることで利用可能。
コンパイルされたコードには型チェックは含まれなので、問題なく動作します。

特にORM系に動的にプロパティを生やすライブラリが多いのですが、それらをTypescriptでどう扱うのかは要確認。
コンパイルを通すために、何らかの型定義が必要なのかも。

React + Typescript

React自体がコンポーネントを定義して組み立てるという思想なので、各コンポーネントのstateやpropsが型で明示的に示されるのが非常に便利。
atom-typescriptでは、tsxのテンプレートリテラルでpropsの候補まで表示してくれるのではかどります。
reduxを使ったパターンとかは今後検討する予定。