Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

Microsoft発のaltJS「TypeScript」+Visual Studioで楽々クライアントサイド開発

さらに使いやすく便利になった「Visual Studio」を始めよう! 第3回

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2015/01/30 14:00

 TypeScriptは、マイクロソフトによって開発が進められているスクリプト言語で、コンパイルすることでJavaScriptに変換される、いわゆるaltJS(JavaScriptの代替言語)の一種。2012年10月に最初に発表された後、1年半を経て、2014年4月に初の正式版1.0がリリースされたばかりの、比較的新しい言語です。本稿では、最初にこのTypeScriptが登場するに至った背景と、言語としての基本的な構文規則を、そして後半では、Visual StudioでTypeScriptを利用する方法について解説していきます。

対象読者

 今回の対象読者は、以下のとおりです。

  • JavaScriptによるクライアントサイド開発に携わってきた方
  • TypeScript、CoffeeScript、Dartなど、altJS技術に興味のある方

必要な環境

  • Visual Studio Community 2013、または他エディション

JavaScriptの問題点

 まず、JavaScriptは決して開発生産性の高い言語ではありません。

 例えばJavaScriptにはクラスという概念はありません。代わりに、JavaScript固有のプロトタイプという機能を使って、クラスのようなものを定義する必要があります。これはクラスベースのオブジェクト指向言語に慣れてきた大概の開発者にとって、なじみにくい機能の一つです。また、ある程度の規模のアプリを開発するには欠かせないパッケージ/名前空間の概念もありません。

 さらに、JavaScriptはデータ型に寛容な言語です。寛容、といえば聞こえは良いものの、本格的な開発では、その寛容さがあいまいさを生み、結果としてバグの潜在的な原因になるおそれがあります。

 にも関わらず、JavaScriptを使い続けなければならないのはなぜか。それは、JavaScriptがブラウザ上で動作する唯一の言語であり、当面、選択の余地がないからです。しかも、「HTML5の普及」をはじめ、「Flash/SilverlightなどRIA(Rich Internet Application)の衰退」、「SPA(Single Page Application)の浸透」などの要因から、近年、Webアプリでクライアント開発が占める割合はいや増しています。JavaScriptの開発生産性を補う手段の提供は急務であったわけです。

 もっとも、これだけ普及しているJavaScriptをいきなり別の言語で挿げ替えるのは現実的ではありません。次期JavaScriptと呼ばれるECMAScript 6も検討されていますが(注1)、これが正式リリースされ、対応ブラウザが十分に普及するには、まだまだ時間が必要でしょう。

 そこで登場するのが、altJS(JavaScriptの代替言語)と呼ばれるアプローチです。JavaScriptに薄い皮(言語)を被せてしまい、JavaScript特有の使いにくさを覆い隠してしまおうという考え方です。例えばaltJSの多くでは、C#、Javaライクなクラスベースのオブジェクト指向構文を利用できます。

 altJSは、一般的には、コンパイラーによってあらかじめJavaScriptに変換されたものが実行されますので、標準的なブラウザだけで動作します。プラグインなどのインストールは必要ありません。

図1 altJSによるソース実行の流れ

 その中では、いよいよclassキーワードも追加される予定です。

altJSとTypeScript

 代表的なaltJSには、Rails 3.1以降で標準搭載されたことから一挙に普及したCoffeeScriptをはじめ、Googleが開発したDart、JavaScriptだけでなくJava/C++/PHPなどにも変換可能なHaxe、そして、本稿で扱うTypeScriptなどがあります。それぞれが鎬を削っており、現時点では、デファクトスタンダードが確立するまでには至っていません。

 そのような状況を前提として、ではありますが、著者がTypeScriptをaltJSの本命と考える理由をいくつか挙げておきます。

(1)JavaScriptのスーパーセットである

 ざっくりと言ってしまうならば、TypeScriptは、JavaScriptの標準仕様であるECMAScript 5に対して、静的な型付けとクラスベースのオブジェクト指向を加えた「JavaScriptのスーパーセット」です。よって、既存のJavaScriptのコードが、ほぼそのまま(比較的少ない修正で)TypeScriptのコードとして動作します。

 オブジェクト指向構文は、次期ECMAScript 6の仕様を先取りした内容になっていますので、TypeScriptを学ぶということは次世代のJavaScriptを学ぶということにも繋がります。

(2)開発環境の選択肢が豊富である

 マイクロソフト発のaltJSということで、まず、Visual Studioとの親和性はピカイチです。本稿後半でも述べるように、自動コンパイル機能をはじめ、構文ハイライト、Intellisense、デバッグ機能など、開発に必要な機能が手厚くサポートされています。

 ただし、TypeScriptをサポートしているのは、なにもVisual Studioだけではありません。Node.jsでもコンパイラーがパッケージ提供されていますし、Sublime TextWebStormEclipseなど、メジャーな開発環境がすでにTypeScriptをサポートしており、また、多くの開発者がこれを利用しています。TypeScriptは、.NET開発者のためだけの言語ではありません。

(3)開発生産性を向上する型システム

 TypeScriptは、その名のとおり、型付けされたJavaScriptです。JavaScriptではあいまいであった型を、TypeScriptでは宣言時に明示するのが基本です(ただし、必須ではありません)。これを「静的型付け」と言います。

 この型システムによって、TypeScriptは統合開発環境との親和性に優れます。C#/Javaなどの言語では当たり前であった型チェック、コード補完の恩恵を当たり前のように享受できます。これまでは実行するまで検出できなかった問題を、コンパイル時、もしくはエディター上でのタイプ時に認識できるため、デバッグ時の負担が大幅に軽減されます。

TypeScriptの基本

 TypeScriptの概要を理解したところで、具体的なコードでもってTypeScriptの威力を確認していきましょう。以下は、

  • toStringメソッドを持ったAnimalクラス
  • Animalクラスを継承したHamsterクラス(独自のメソッドとしてwalkを公開)

を定義したTypeScriptのコードです。

 比較のために、コンパイル済みのJavaScriptのコード(注2)も併記しておきます(内容はコメントでざっと把握できれば十分です)。JavaScriptでは冗長にならざるを得なかったコードが、TypeScriptによってぐんとコンパクトに、しかも、C#、Javaなどに慣れた開発者にとっても直感的に分かりやすいものになったことが確認できるはずです。

 Visual StudioでのTypeScriptコードの作成/コンパイル方法については、次節で改めます。

リスト1 Animal/Hamsterクラスを定義したコード(上:TypeScriptのコード、下:コンパイル済みのJavaScript)
// (1)CodeZineSampleモジュールを定義
module CodeZineSample {
  // (2)Animalクラスを定義
  export class Animal {
    // (4)コンストラクターでname/ageフィールドを初期化
    constructor(public name: string, public age: number) { }

    // (5)toStringメソッドを定義
    public toString(): string {
      return this.name + ':' + this.age + '歳';
    }
  }

  // (3)Animalクラスを継承したHamsterクラスを定義
  export class Hamster extends Animal {
    // (6)walkメソッドを定義
    public walk(): string {
      return this.name + 'はトコトコ歩いています。';
    }
  }
}

// (7)CodeZineSample.Hamsterクラスのメソッドを呼び出し
import s = CodeZineSample;
var h = new s.Hamster('トクジロウ', 15);
console.log(h.toString());	// 結果:トクジロウ:15歳
console.log(h.walk());	// 結果:トクジロウはトコトコ歩いています。
-----------------------------------------------------------------------
// 継承のための関数を準備
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};

// CodeZineSampleモジュールを定義
var CodeZineSample;
(function (CodeZineSample) {
  // Animalクラスを定義
  var Animal = (function () {
    // コンストラクターでname/ageフィールドを初期化
    function Animal(name, age) {
      this.name = name;
      this.age = age;
    }
    // toStringメソッドを定義
    Animal.prototype.toString = function () {
      return this.name + ':' + this.age + '歳';
    };
    return Animal;
  })();
  CodeZineSample.Animal = Animal;

  // Animalクラスを継承したHamsterクラスを定義
  var Hamster = (function (_super) {
    __extends(Hamster, _super);
    // 基底クラスのコンストラクターを呼び出し
    function Hamster() {
      _super.apply(this, arguments);
    }
    // walkメソッドを定義
    Hamster.prototype.walk = function () {
      return this.name + 'はトコトコ歩いています。';
    };
    return Hamster;
  })(Animal);
  CodeZineSample.Hamster = Hamster;
})(CodeZineSample || (CodeZineSample = {}));

// CodeZineSample.Hamsterクラスのメソッドを呼び出し
var s = CodeZineSample;
var h = new s.Hamster('トクジロウ', 15);
console.log(h.toString());
console.log(h.walk());
//# sourceMappingURL=myApp.js.map

 以降では、コードの詳細を掻い摘んでみていきましょう。

モジュールとクラス

 まず、(1)は内部モジュールの定義です(注3)。いわゆるC#の名前空間、Javaのパッケージに相当する要素で、moduleキーワードによって表します。

 モジュールの中には、クラス(class)/インターフェイス(interface)/列挙型(enum)など、これまたオブジェクト指向開発にはおなじみの要素を定義できます。例えば(2)ではclassキーワードを利用して、Animalクラスを定義しています。(3)のように、extendsキーワードを利用することで継承を表現することもできます。サーバーサイドの開発者でもごく直感的に理解できる、これらオブジェクト指向構文は、TypeScriptのもっとも重要な機能の一つです。

 classキーワードの直前にあるexportキーワード((2))は、モジュールの外からクラスを参照できることを意味します。exportキーワードがない場合、TypeScriptはモジュール配下の要素へのアクセスを許可しませんので、注意してください。

 内部モジュールというからには外部モジュールもあるわけですが、こちらはクライアントサイド開発ではあまり利用しませんので、まずはあまり気にしなくて構いません。

コンストラクターとメソッド

 Animalクラスの配下では、コンストラクターとtoStringメソッドを定義しています。TypeScriptでは、コンストラクターの名前はconstructorで固定です。

 コンストラクターの引数にも注目してください((4))。冒頭で触れたように、TypeScriptでは静的型付けに対応した言語です。「仮引数名: 型名」の形式でデータ型を宣言しておくことで、想定外の型は設定できないようになります。型の指定は任意ですが、TypeScriptを使っているならば、あえて明示しない意味はありません。型名として利用できるのは、string、number、booleanなどのプリミティブ型の他、任意のオブジェクト型です。

 引数に付与されたpublicキーワードは、アクセス修飾子です。TypeScriptでは、コンストラクターの引数にpublic/privateなどのアクセス修飾子を付けることで、これを対応するメンバー変数(フィールド)として設定するという決まりがあります。よって、(4)の例であれば、「string型のnameフィールド、number型のageフィールドをpublic権限で定義、引数の値で初期化しなさい」という意味になります。あえて冗長に表すならば、以下のコードとほぼ同意です。

リスト2 (4)のコードをあえて冗長に書き換えたコード
public name : string;
public age : number;

constructor(name: string, age: number) {
  this.name = name;
  this.age = age;
}

 コンストラクターの主な役割がメンバー変数の初期化であることを考えれば、これだけのコードを省力化できるのはなかなかに嬉しい仕組みですね。

 toString((5))、walk((6))メソッドのように一般のメソッドでは、「: string」のように、メソッドシグニチャーの中で戻り値の型を明示的に指定することも可能です。

インポート宣言

 モジュール配下のクラスをインスタンス化する場合、「new CodeZineSample.Hamster(……)」のように完全修飾名で表しても構いませんが、モジュールの階層が深くなってくると自ずと名前も長くなり、コードの可読性を損なう恐れがあります。

 そこでTypeScriptでは、import命令を利用することで、モジュールに別名を付与できます。この例であれば、CodeZineSampleモジュールに対して「s」という別名を与えています((7))。これによって、「new s.Hamster(……)」のような表記が可能になるわけです。

 以上が、TypeScriptの基本的な構文ルールです。もちろん、ここで触れた内容はごく大雑把なものなので、より詳しい文法/構文については、以下のサイトも参照してください。記事末尾では、関連書籍も紹介しています。

Visual Studioを使ったTypeScript開発

 TypeScriptによるコーディングの基本が理解できたところで、Visual Studioを使った開発の手順を見ていくことにしましょう。Visual StudioはMSDNサブスクリプションを持っていれば、プランに応じて最新のバージョンを利用できますし、さもなければ、無償のCommunity版をダウンロードしても構いません。Community版は2014年11月に公開されたばかりの新たなエディションで、Professional版に相当する機能を無償(注4)で利用できます。

 具体的な手順を追っていく中で、Visual StudioのTypeScript対応機能を確認してみてください。

 ライセンスの詳細は、上記のページから確認してください。

TypeScript開発のためのプロジェクトテンプレート

 TypeScriptを利用した開発を行うために、Visual Studioでは専用のプロジェクトテンプレートを用意しています。[新しいプロジェクト]ダイアログで[TypeScript]-[TypeScriptを利用したHTMLアプリケーション]を選択してください。

図2 [新しいプロジェクト]ダイアログ
[新しいプロジェクト]ダイアログ

 これによって、TypeScriptの簡単なサンプルと、これを起動するためのテストページを含んだシンプルなプロジェクトが作成されます。純粋にTypeScriptを利用するならば、まずはこちらのプロジェクトを利用するのが素直です。

図3 [TypeScriptを利用したHTMLアプリケーション]の初期状態
[TypeScriptを利用したHTMLアプリケーション]の初期状態

 もちろん、ASP.NETを利用しているならば、従来の[Web]-[ASP.NET Webアプリケーション]をそのまま選択しても構いません。

TypeScriptファイルの作成 ―― 構文ハイライト/コード補完機能

 TypeScriptファイル(.tsファイル)を作成するには、ソリューションエクスプローラーから該当のフォルダーを右クリックし、表示されたコンテキストメニューから[追加]-[TypeScriptファイル]を選択するだけです。[項目の名前を指定]ダイアログが開きますので、例えば「myApp」のような名前を入力して[OK]ボタンをクリックします。

図4 [項目の名前を指定]ダイアログ
[項目の名前を指定]ダイアログ

 空のmyApp.tsがコードエディターから開きますので、例えば先ほどのリスト1を入力してみましょう。C#/Visual Basicなどを編集する場合と同じく、

  • 構文ハイライトが働いている点
  • 型に応じて適切なメンバー候補が表示されている点
  • 誤った型の値を指定した場合にはエラーが報告される点(赤波線が引かれる)

などにも注目してください。

図5 TypeScriptのコードをコードエディターで編集する
TypeScriptのコードをコードエディターで編集する

 ちなみに、自作のメソッド(関数)でもあらかじめJSDoc形式のドキュメンテーションコメントを残しておくことで、コード補完時にツールヒントを表示させることができます。例えば、以下はコンストラクターにドキュメンテーションコメントを追加したコードと、その時のコードエディターでの表示です。

リスト3 ドキュメンテーションコメントを追加したコード(myApp.ts)
/**
* コンストラクター
* @param name 動物の名前
* @param age 動物の年齢
*/
constructor(public name: string, public age: number) { }
図6 引数を入力する際に、その詳細情報がツールヒントとして表示される
引数を入力する際に、その詳細情報がツールヒントとして表示される

 タイプ時に引数の意味が分かりやすくなりますので、メソッド/関数などを定義する際には積極的にドキュメンテーションコメントを利用することをお勧めします。

TypeScriptの保存&実行 ―― 自動コンパイル

 コードを入力できたら、(myApp.tsの保存)ボタンでファイルを保存します。Visual Studioでは、このタイミングで自動的にコンパイルが実施され、myApp.jsとmyApp.js.mapが生成されます。myApp.jsがコンパイル済みのJavaScriptコード、myApp.js.mapがソースマップ(注5)です。

 ただし、これらのファイルはデフォルトではプロジェクトには組み込まれません。ソリューションエクスプローラーから(すべてのファイルを表示)ボタンを有効にするか、標準のエクスプローラーから確認してください。

 TypeScriptコードとコンパイル済みのJavaScriptコードをマッピングするための情報ファイルです。ソースマップを利用することで、デバッグ時にもエラー箇所などをJavaScriptコードではなく、元々のTypeScriptコードから確認できます。

 TypeScriptのコードを利用するには、該当のページ(.htmlファイル)からコンパイル済みの.jsファイルをインポートします(.tsファイルではありません!)。ここでは、もともとプロジェクトに用意されているindex.htmlに対してmyApp.jsを追加します。

 <script>要素は自分でタイプしても構いませんが、ソリューションエクスプローラーからコードエディターに.jsファイルをドラッグ&ドロップすることで、コードを自動生成することもできます。

図7 .jsファイルをコードエディターにドラッグ&ドロップ
.jsファイルをコードエディターにドラッグ&ドロップ

 サンプルを実行するには、ボタンをクリックします。ここではInternet Explorerを選択していますが、から使用するブラウザを選択することもできます。クライアントサイド開発では、クロスブラウザでの動作確認が欠かせませんので、このように統合開発環境からブラウザを簡単に切り替えられるのは嬉しいポイントです。

図8 デバッグ実行に利用するブラウザを選択する
デバッグ実行に利用するブラウザを選択する

TypeScriptのデバッグ ―― ブレイクポイント/ステップ実行

 デバッグ時には、ブレイクポイントを設定することもできます。コードエディター左のラインをクリックするか、該当する行で[F9]ボタンをクリックしてください。

図9 TypeScriptのコードにブレイクポイントを設置
TypeScriptのコードにブレイクポイントを設置

 この状態でアプリを実行すると、確かにブレイクポイントで動作が止まっていることが確認できます。TypeScript側で設定したブレイクポイントが、JavaScriptコードの実行に反応できるのも、先ほど自動生成されたソースマップのおかげです。

図10 ブレイクポイントでアプリが停止した状態
ブレイクポイントでアプリが停止した状態

 もちろん、(ステップイン)、(ステップオーバー)、(ステップアウト)でステップ実行することもできますし、その際に[ローカル]ウィンドウで現在の変数の状態を確認することもできます。TypeScriptでは型を厳密に認識できますので、[ローカル]ウィンドウでも型を含んだ情報を確認できる点に注目です。

 C#/Visual Basicの開発に慣れた方ならば(というよりも、類似の統合開発環境を利用したことがある方ならば)、これまでの知識の範囲で操作できることがお分かりになると思います。

ASP.NETアプリケーションの場合

 ASP.NET MVCなどでTypeScript(JavaScript)開発を行っている場合、バンドル&ミニフィケーション機能(注6)を無効にしておく必要があります。さもないと、以下のようなエラーが出て、ブレイクポイントが有効になりませんので、注意してください。
 

図11 ブレイクポイントが有効にならない
ブレイクポイントが有効にならない

 バンドル&ミニフィケーション機能を無効にするには、App_Start/BundleConfig.csを以下のように編集してください。

リスト4 バンドル&ミニフィケーション機能を無効にする(BundleConfig.cs)
public static void RegisterBundles(BundleCollection bundles)
{
  ……中略……
  BundleTable.EnableOptimizations = false;
}

 複数のJavaScriptのコードを束ね(bundle)、コメント/改行などを取り除いて圧縮(minify)する機能のことです。

型定義ファイルでJavaScriptライブラリを利用する

 TypeScriptでは、既存のJavaScriptライブラリとの相互運用性についても考慮されています。JavaScriptライブラリではTypeScriptのように型情報を持ちませんので、型情報だけを別に提供することで両者の橋渡しとするわけです。このような役割を持ったファイルのことを型定義ファイルといい、.d.tsファイルとして提供されます。

 「DefinitelyTyped」というサイトで、jQuery、AngularJSをはじめ、現在主流と言われるJavaScriptライブラリの型定義ファイルが提供されていますので、興味のある方は、どのようなものがあるのか、眺めてみると良いでしょう。

図12 DefinitelyTyped
DefinitelyTyped

 型定義ファイルを組み込むことで、TypeScriptからJavaScriptライブラリを利用できるようになるだけでなく、タイプ時にコード補完/ツールヒントが有効になるというメリットもあります。

図13 TypeScriptからjQueryを呼び出した例
TypeScriptからjQueryを呼び出した例

 型定義ファイルをプロジェクトに組み込むのはカンタン、[パッケージマネージャーコンソール]ダイアログから以下のコマンドを実行します。例えば以下はjQuery本体と、その型定義ファイルをインストールする例です。

リスト5 jQuery本体と型定義ファイルのインストール例
PM> Install-Package jQuery	(jQuery)
PM> Install-Package jQuery.TypeScript.DefinitelyTyped	(jQuery型定義ファイル)

 すでに元となるライブラリ(ここではjQuery)がインストールされている場合には、ソリューションエクスプローラーから「jquery-x.x.x.js」を右クリックし、表示されたコンテキストメニューから[TypeScript型指定の検索...]を選択しても構いません。

図14 [<プロジェクト名> - NuGetパッケージの管理]ダイアログ
[<プロジェクト名> - NuGetパッケージの管理]ダイアログ

 [<プロジェクト名> - NuGetパッケージの管理]ダイアログが表示されますので、「jQuery.TypeScript.DefinitelyTyped」から[インストール]ボタンを選択します。

 インストールされた型定義ファイルは、/Scripts/typings/jqueryフォルダーの配下に配置されますので、これをコードエディター上で開いた.tsファイルにドラッグ&ドロップしてください。

図15 型定義ファイルをコードエディターにドラッグ&ドロップ
型定義ファイルをコードエディターにドラッグ&ドロップ

 先頭に「/// <reference path="scripts/typings/jquery/jquery.d.ts" />」が追加されれば、正しく型定義ファイルを紐付けできています。あとは、先ほどの図13のように、jQueryのメソッドに対してメソッド候補、ツールヒントが表示されるようになります。

 なお、<reference>要素による型定義ファイルの紐付けは、あくまで型情報の紐付けにすぎません。.htmlファイルからjQueryを利用するには、本来のjquery-x.x.x.jsをインポートしなければならない点に注意してください。

補足:TypeScript Playground

 TypeScriptを勉強するならば、本家サイトで提供されている「TypeScript Playground」を利用するのも手です。

 TypeScript Playgroundは、ブラウザー上で動作する簡易インタプリターで、左枠にTypeScriptのコードを入力すると、右枠にリアルタイムでコンパイル済みのJavaScriptコードが表示されます。入力したコードは、右肩の[Run]ボタンをクリックすることで、その場で実行することも可能です。

 JavaScriptに慣れている人であれば、TypeScript/JavaScript双方の対応関係を確認しながら学習を進められるので、理解も深まりやすいでしょう。左上の選択ボックスからは典型的なコード例を選択/参照することもできます。

まとめ

 TypeScriptのようなaltJSの存在は、今後ますます複雑になっていくクライアントサイド開発の分野では欠かせないものになっていくはずです。いつまでも「なじみにくい」JavaScriptに拘泥されている必要はありません。本来の生産性に関係ないJavaScriptの難しさはaltJSの甘い皮で包んでもらって、私たち開発者はより付加価値の高い関心事に重心を傾けるべきです。

 冒頭述べたように、altJSの世界はまだまだ群雄割拠、必ずしもデファクトスタンダードが確立した世界ではありませんが、皆さん自身の目でどれが学ぶに値する技術であるかを見極めてみてください。本稿が、その一助となれば幸いです。

関連書籍・サイト

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • WINGSプロジェクト(ウイングスプロジェクト)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XMLD...

All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5