Gulp で TypeScript をコンパイル & tsify で連結 - Gulp で作る Web フロントエンド開発環境 #4
wakamsha
TypeScript とは
TypeScript はマイクロソフトによって開発されたフリーでオープンソースのプログラミング言語である。TypeScript は JavaScript に使用するかどうかが任意の静的型付けとクラスベースオブジェクト指向を加えたスーパーセットとなっている。C# のリードアーキテクトであるアンダース・ヘルスバーグが TypeScript の開発に関わっている。
Alt JS として何かと CoffeeScript と比較されることの多い言語です。これまでは Rails 開発が主だったことからデフォルトで対応している CoffeeScript を使うことが多かったのですが、最近アサインされた案件は Rails ではなく Scala & Play による開発なので、同じ型推論を持つ TypeScript を使ってみようぜということになりました1)特に深い意味はありません。。
サンプルコードはこちらからどうぞ。
TypeScript を Gulp 上でコンパイルしてみる
まずは TypeScript コンパイラをインストールします。
$ npm install -g typescript
$ tsc -v
message TS6029: Version 1.5.0-alpha
gulp-typescript をインストールします。
$ npm i -D gulp-typescript
適当に TypeScript ファイルを作成します。公式サイトにあるチュートリアルのコードをそのまま持って来ました。
class Student {
fullname : string;
constructor(public firstname, public middleinitial, public lastname) {
this.fullname = `${firstname} ${middleinitial} ${lastname}`;
}
}
interface Person {
firstname: string;
lastname: string;
}
function greeter(person : Person) {
return `Hello, ${person.firstname} $person.lastname}`;
}
var user = new Student('Jane', 'M.', 'User');
document.body.innerHTML = greeter(user);
タスクを定義します。
var typescript = require('gulp-typescript');
gulp.task('ts', () => {
return gulp.src('./src/typescripts/**/*.ts')
.pipe(typescript({
target: 'ES5',
removeComments: true
}))
.js.pipe(gulp.dest('./dest/assets/js'));
});
いくつかのコンパイルオプションを指定してみました。
target
- ECMAScript 構文を指定。デフォルトは
ES3
で、ES5
,ES6
が選択可能
- ECMAScript 構文を指定。デフォルトは
removeComments
- コメントを削除するかどうかを指定します。デフォルトは
false
- コメントを削除するかどうかを指定します。デフォルトは
こちらのサイトにて全てのオプションを確認することが出来ます。
実行してみます。
$ gulp ts
[11:37:09] Using gulpfile ~/Documents/sandbox/try_gulp/try_typescript/gulpfile.js
[11:37:09] Starting 'ts'...
[11:37:10] Finished 'ts' after 1.23 s
生成された JavaScript がこちら。
var Student = (function () {
function Student(firstname, middleinitial, lastname) {
this.firstname = firstname;
this.middleinitial = middleinitial;
this.lastname = lastname;
this.fullname = firstname + " " + middleinitial + " " + lastname;
}
return Student;
})();
function greeter(person) {
return "Hello, " + person.firstname + " " + person.lastname;
}
var user = new Student("Jane", "M.", "User");
document.body.innerHTML = greeter(user);
さすが TypeScript 。きれいなコードにコンパイルされました。
モジュール単位で分割したファイルを読み込む
TypeScript を取り入れる主な動機に、肥大化するコードをモジュール単位で分割して管理したいというのがあります。モジュールの定義方法はいくつかありますが、ここでは内部モジュールと外部モジュールの二種類について学ぶとします。
内部モジュール (Internal Module)
早い話が、名前空間を定義するというものです。これによってオブジェクトやクラスに境界線や階層を作ることができ、名前の衝突を防いだり構造が管理しやすくなります。
簡単なモジュールを作ってみます。
module app.util.strings {
export function trimLeft(str: String): string {
return str.replace(/^s+/, '');
}
export function trimRight(str: String): string {
return str.replace(/s+$/, '');
}
}
app
という変数がグローバルスコープに追加されます。モジュールの内容は全てスコープ内に収まっており、デフォルトでは private
となっているので通常は外部から参照出来ませんが、export
というキーワードをメンバの先頭につけることでpublic
となり、参照できるようになります。
また、モジュールは閉じないので内部モジュールを複数のファイルに分割して定義することも可能です。そのため各ファイルを管理しやすいサイズに保つことが出来ます。
モジュールを読み込むファイルを作ります。
/// <reference path='util/strings.ts' />
(function() {
var foo = ' foo';
var bar = 'bar ';
console.log(app.util.strings.trimLeft(foo));
console.log(app.util.strings.trimRight(bar));
})();
TypeScript はコンパイル時に型情報が必要なので、trimLeft()
関数の型情報が必要になります。そのため、コードの先頭に///
から始まる reference タグを記述して、参照したい型情報のファイルパスを記述する。
ちなみにこのままコンパイルするとapplication.ts
とutil/strings.ts
がそれぞれ別ファイルとしてコンパイルされるため、HTML側で両方とも読み込む必要があります。手間がかかる上にパフォーマンス的にもよろしくないので、1つのファイルに連結するのがセオリーです。タスクを以下のように定義しなおします。
gulp.task('ts', () => {
return gulp.src('./src/typescripts/**/*.ts')
.pipe(typescript({
target: 'ES5',
removeComments: true,
out: 'app.js'
}))
.js
.pipe(gulp.dest('./dest/assets/js'));
});
out
オプションに生成する JS ファイル名を指定します。これにより、複数の TS ファイルが一つの JS ファイルに連結された状態で生成されます。連結された JS ファイルは reference タグで指定された順序で参照ファイルを読み込まれ、依存関係が適切に処理されます。前回の記事で紹介した Browserify の機能と同じですね。
コンパイル後の JS ファイルはこちら。
var app;
(function (app) {
var util;
(function (util) {
var strings;
(function (strings) {
function trimLeft(str) {
return str.replace(/^s+/, '');
}
strings.trimLeft = trimLeft;
function trimRight(str) {
return str.replace(/s+$/, '');
}
strings.trimRight = trimRight;
})(strings = util.strings || (util.strings = {}));
})(util = app.util || (app.util = {}));
})(app || (app = {}));
(function () {
var foo = ' foo';
var bar = 'bar ';
console.log(app.util.strings.trimLeft(foo));
console.log(app.util.strings.trimRight(bar));
})();
比較的仕組みがシンプルで分かりやすいのが内部モジュールの特徴です。グローバル変数が一つ生成されてしまうのがデメリットですが、許容できるのであれば有用な手法です。
外部モジュール (External Module)
CommonJS や Asynchronous Module Definition (AMD) が定める仕様に従ってモジュールをエクスポート/インポートする仕組みです。
export function trimLeft(str: String): string {
return str.replace(/^s+/, '');
}
export function trimRight(str: String): string {
return str.replace(/s+$/, '');
}
外部モジュールを使用する場合は、モジュールはファイルによって表されます。したがってコードを module {}
で囲む必要がなくなります。
外部モジュールを使用するには、 require
関数の呼び出しに import
キーワードを指定します。この箇所はコンパイル時にモジュールを読み込むコードに変換されます。
import strings = require('./util/strings');
var foo = ' foo';
var bar = 'bar ';
console.log(strings.trimLeft(foo));
console.log(strings.trimRight(bar));
tsify - TypeScript をコンパイルするための Browserify プラグイン
Browserify によって JS ファイルの依存関係を解決 (適切な順序で JS ファイルを連結する) することが出来ますが、tsify はこれに TypeScript のコンパイル機能を追加することが出来るプラグインです。
Node パッケージをインストールします。
$ npm i -D tsify
タスクを定義します。
gulp.task('tsify', () => {
return browserify()
.add('./src/typescripts/external/application.ts')
.plugin('tsify', {
target: 'ES5',
removeComments: true
})
.bundle()
.pipe(source('app_external.js'))
.pipe(gulp.dest('./dest/assets/js'));
});
tsify
にも gulp-typescript のようにオプションを渡すことが出来ます。渡せるオプションは、tsc
コマンドに渡すオプションと同じです。$ tsc -hで内容を確認できます。
実行してみます。
$ gulp tsify
[20:40:17] Using gulpfile ~/Documents/sandbox/try_gulp/try_typescript/gulpfile.js
[20:40:17] Starting 'tsify'...
[20:40:18] Finished 'tsify' after 1.01 s
コンパイル後の JS はこちら。
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var strings = require('./util/strings');
var foo = ' foo';
var bar = 'bar ';
console.log(strings.trimLeft(foo));
console.log(strings.trimRight(bar));
},{"./util/strings":2}],2:[function(require,module,exports){
function trimLeft(str) {
return str.replace(/^s+/, '');
}
exports.trimLeft = trimLeft;
function trimRight(str) {
return str.replace(/s+$/, '');
}
exports.trimRight = trimRight;
},{}]},{},[1]);
こちらは内部モジュールと違って全体が関数スコープで囲われました。 tsify を使わずに Browserify だけでコンパイルすると define()
関数の関係で require.js が必要になるみたいなので、このプラグインには助けられます。
これで TypeScript を学習するための環境が整いましたので、コツコツ勉強していきたいと思います。
脚注
↑1 | 特に深い意味はありません。 |
---|