TypeScriptで始めるNode.js/io.js入門
こんにちは、ICSの鹿野です。
みなさんは普段Node.jsをどのように使っていますか? サーバーサイドで動くJavaScriptであるNode.jsは、Socket.IOと連携してリアルタイムなWebコンテンツを作ったり、Grunt・gulpのようなフロントエンドツールとして使ったりとさまざまな場面で活用されています。ですが、Node.jsでコーディングする際は、JavaScriptの特性ゆえに規模が大きくなるほどコードの見通しが悪くなり思わぬバグの原因となります。それを解決して見通しのよいJavaScriptコードを書けるようにしてくれるのがTypeScriptです。今回はNode.jsをTypeScriptで記述する基本からスタートし、TypeScriptの外部モジュール機能を使ってNode.jsの処理を複数ファイルに分割するまでを紹介します。今回のサンプルコードはGithubで公開してあります。
Node.jsとTypeScriptをインストールしよう
使用環境を整えましょう。まずNode.jsをインストールします。Node.jsは公式サイトのインストーラーを使って手順に従ってインストールしていきます。インストール後コマンドラインにてnode -vと実行し、Nodeのバージョンが表示されていればインストール成功です。ちなみに先日Node.jsからフォークされたio.jsも、Node.jsと互換性があるので今回説明するコードを全て流用できます(※)。つづいて、TypeScriptをインストールします。コマンドラインから以下のコマンドを実行します。OS Xでエラーが出る場合は管理者権限(sudo)で試してみてください。
※ Node.js:v0.10.35 / io.js:v1.0.4で検証。完全な互換性があるわけではありません。
npm install -g typescript
以上でNode.jsをTypeScriptで記述する環境の構築は終了です。ではさっそくNode.jsをTypeScriptで記述していきましょう。任意のフォルダを準備し、その中に新規テキストファイルを作成してapp.tsという名前にします。app.tsの中にテキストエディターで以下のようなコードを記述します。
console.log("Hello! Node.js × TypeScript");
コマンドラインでこのapp.tsがあるフォルダに移動してTypeScriptをコンパイルするためのtscコマンドを実行するとTypeScriptのapp.tsがJavaScriptのapp.jsにコンパイルされます。例えば/Users/MyName/sample_serverに保存したapp.tsをapp.jsにコンパイルするコマンドは以下です。
# Macでの移動(Windowsの場合は読み替えてください) cd /Users/MyName/sample_server # app.tsのコンパイル tsc app
app.jsができたらそれをNode.jsとして実行ます。コマンドラインでNode.jsを実行するための「node」コマンドを実行します。コマンドラインに「Hello! Node.js × TypeScript」と出力されるのが確認できます。
node app
処理を機能毎にひとまとめにしてクラス化しておくとコードの見通しがよくなります。上記の処理をクラス化しておきましょう。
class Main // classを指定してクラス化 { // コンストラクター constructor() { console.log("Hello! Node.js × TypeScript from Class"); } } // Mainクラスのインスタンスを作る var main:Main = new Main();
(サンプル:TypeScriptで記述したNode.jsで文字列出力)
httpモジュールを使ってhttpサーバーを立ててみよう
さて、ここからはNode.jsをTypeScriptで記述して簡単なhttpサーバーを立ててみましょう。ブラウザでアクセスがあったら文字列を表示するシンプルなhttpサーバーです。Node.jsにおける様々な処理は「モジュール」という単位に分けられています。例えばhttpサーバーを立てるための機能はhttpモジュール、ファイルの読み書きを行うための機能はfsモジュールにて定義されています。TypeScriptでこのようなモジュールを使用するためにはimport文とrequire()メソッドを使います。
import http = require("http"); // Node.jsのhttpモジュールを読み込み
httpサーバーを設定するcreateServer()メソッド、サーバーを起動してリクエストを待ち受け状態にするlisten()メソッドを使って、httpサーバーは以下のようにして構築することができます。
import http = require("http"); class Main { constructor() { // httpサーバーを設定する var server:http.Server = http.createServer((request, response) => this.requestHandler(request, response)); // サーバーを起動してリクエストを待ち受け状態にする server.listen("5000"); } /* * サーバーにリクエストがあった時に実行される関数 */ private requestHandler(request, response):void { response.end("Hello! Node.js with TypeScript"); } } var main:Main = new Main();
imort文とrequire()メソッドを使ってモジュールを読み込んだ場合、コンパイルコマンドtscにオプションとして「–module commonjs」(※1)を付加する必要があります。TypeScriptにおけるこのようなモジュールの取り扱い方をTypeScriptの外部モジュール機能(※2)と言います。
tsc app --module commonjs
ですが、上記のコマンドを実行すると「Cannot find external module ‘http’」というエラーが出ます。これはTypeScriptがNode.jsのhttpモジュールにどんなメソッドや変数があるかという情報を持っていないためです。そこで、これらの情報が定義された型定義ファイルというものを使います。DefinitelyTypedのGithubからNode.jsの型定義ファイルnode.d.tsをダウンロードして、app.tsと同じ階層に配置します。型定義ファイルはreferenceタグを使って以下のように読み込みます。型定義ファイルを読み込むとWebStorm等のTypeScriptのコーディングに対応したエディターでコード補完が効くようになるというメリットもあります。
// Node.jsの型定義ファイルの読み込み /// <reference path="./node.d.ts" />;
以上よりコードの最終形は以下の形になります。なお型の指定(request:http.ServerRequest等の:http.ServerRequestの部分)は無くても動きます。
/// <reference path="./node.d.ts" />; import http = require("http"); class Main { constructor() { var server = http.createServer((request:http.ServerRequest, response:http.ServerResponse) => this.requestHandler(request, response)); server.listen("5000"); } /* * サーバーにリクエストがあった時に実行される関数 */ private requestHandler(request:http.ServerRequest, response:http.ServerResponse):void { response.end("Hello! Node.js with TypeScript"); } } var main:Main = new Main();
コンパイルが終了したらコマンドラインからnode appを実行し、ブラウザでhttp://localhost:5000にアクセスすると「Hello! Node.js with TypeScript」と表示されています。これはNode.jsによってhttpサーバーが起動してクライアントのリクエストを待っている状態であることを示します。この待ち受け状態はショートカット[Ctrl]+[C]キーで停止できます。
これで簡易なhttpサーバーが構築できましたが、実際のサーバーはリクエストに応じてHTMLファイルや画像ファイルを返す処理(ルーティング処理)が必要になります。参考までに、これらのルーティング処理を行ってhttpサーバーでHTMLとCSSと画像が読み込まれるサンプルコードをGithubにアップしておきました。
(サンプル:httpサーバーでHTMLファイル表示)
※1 TypeScriptにおける外部モジュールはCommonJS方式とAMD方式という2種類があります。CommonJSはNode.jsで外部モジュールを扱う方式、AMDはブラウザで外部モジュールを扱う方式です。
※2 複数のTypeScriptファイルを大きな一つのファイルとして扱うための内部モジュール機能というものもありますが、Node.jsでは使用できません。
外部モジュールを作って長い処理を複数のファイルに分けよう
処理が増える毎に長くなっていくスクリプトの見通しを良くするためには、機能毎に処理を分けて外部ファイル化してオリジナルの外部モジュールをつくるといいです。上記のhttpサーバーを立てる処理をServerAPIクラスという名前で定義し、app.tsと同じ階層にserverModule.tsという外部ファイルを作ってapp.tsのMainクラスから読み込みます。ここで注意したいのは、外部から読み込まれるクラスにはclassの前にexportを指定する必要があることです。
serverModule.ts
/// <reference path="./node.d.ts" />; import http = require("http"); export class ServerAPI { public initServer():void { var server:http.Server = http.createServer((request:http.ServerRequest, response:http.ServerResponse) => this.requestHandler(request, response)); server.listen("5000"); } /* * サーバーにリクエストがあった時に実行される関数 */ private requestHandler(request:http.ServerRequest, response:http.ServerResponse):void { response.end("Call From ServeAPI Class"); } }
これを読み込む側のapp.tsの処理ですが、Node.jsのモジュール読み込みと同じようにimport文とrequire()メソッドを使います。
app.ts
// 外部tsファイルserverModule.tsを読み込み、serverModuleという名前をつける。 import serverModule = require("./serverModule"); class Main { constructor() { // serverModuleの中のServerAPIクラスのインスタンスを作成 var serverAPI:serverModule.ServerAPI = new serverModule.ServerAPI(); // ServerAPIの関数を実行 serverAPI.initServer(); } } var main:Main = new Main();
app.tsをコンパイルし、app.jsを実行します。
コマンドライン
# app.tsをコンパイル tsc --module commonjs app # app.jsを実行 node app
http://localhost:5000にアクセスして「Call From ServeAPI Class」と表示されていたら成功です(※)。外部ファイルserverModule.tsに記述したクラスをメインのクラスから実行できました。
※ サーバーを複数起動しようとするとうまくいかないことがあるので、その場合は不要なサーバーをショートカット[Ctrl]+[C]キーで停止したりコマンドラインやブラウザの再起動を試してみてください。
最後に
いかがでしたでしょうか? Node.jsをTypeScriptで記述すると静的型付けの恩恵に預かることができ、大規模な開発やメンテナンスがやりやすくなります。WebStormやVisual Studioを使えばコード補完やデバッグ機能が使えるので更に便利になるでしょう。なお、今回はNode.jsとの連携の説明に絞ったため、TypeScriptそのものの詳しい説明は省略しましたが、わかめまさひろ氏の書籍「TypeScriptリファレンス Ver.1.0対応」がとてもわかりやすくておすすめなので是非御覧ください。