最新のアプリ

最新のアプリで TypeScript を使用する

Rachel Appel

JavaScript の当初の目的は、小さなドキュメント オブジェクト モデル (DOM) ツリーで実行する DOM 操作でした。しかし、時間の経過と共に JavaScript は人気が高まり、今ではニッチなアプリからエンタープライズ アプリまであらゆる種類のアプリの主流言語となりました。JavaScript の人気が伸び続けていることから、必然的に JavaScript 開発者を支援するために必要なツールや言語の数が増えています。TypeScript も、そのような言語の 1 つです。

TypeScript の概要と動作のしくみ

TypeScript は JavaScript のスーパーセットです。TypeScript を使用すると、より厳密に型指定されてよりオブジェクト指向の動作を実現しながらも、開発者が好む (場合によっては嫌う) JavaScript の柔軟性が保持された JavaScript コードを記述して生成できます。TypeScript を使用すると、JavaScript の利用可能範囲が、エンタープライズ アプリや Web サイトの領域に加えて、関連ツールの不足が原因でこれまでは JavaScript がうまく動作しなかったアプリの領域にまで広がります。

Tsc.exe はオープン ソースの TypeScript コンパイラ兼コード ジェネレーターで、typescriptlang.org(英語) からダウンロードできます。TypeScript はスタンドアロンのコンパイラなので、いつでもコマンド プロンプトを起動して次のように適切な引数を指定すれば tsc.exe を実行できます。

tsc.exe --out outputfile.js inputfile.ts

TypeScript コードを記述し、コンパイラを使用して配置すると、運用環境向けの JavaScript が生成されます。TypeScript はコード ジェネレーターですが、不要なコードの生成 (ビジュアル デザイン ツールではよく発生します)、変数名の破損、または変数の順序変更は発生しません。つまり、最終的な成果物は JavaScript そのものなので、用意にデバッグできます。

JavaScript は既にオブジェクト指向言語ですが、JavaScript のプロトタイプ構文は多くの開発者を悩ませています。この問題を解決するために、TypeScript では、ECMAScript 6 (ES6) 標準で提案されている、クラスやインターフェイスなどの機能を JavaScript に追加します。このため、TypeScript は糖衣構文で覆われたコード ジェネレーターなので、ほとんどの場合は、保守が必要な JavaScript の量が削減されます。たとえば、次のコードではプロトタイプ構文を使用しています。

function Animal(name, species, habitat) {
   this.name = name;
   this.species = species;
   this.habitat = habitat;
 }
 Animal.prototype.sayHello = function(){
   console.log("RAWR!");
 }
 var animal =   new Animal("Fluffy", 
   "Velociraptor ", 
   "Everywhere. Run and hide.");
 animal.sayHello();

上記のサンプルは、コンストラクター関数から始まります。これは JavaScript で頻繁に使用されるパターンで、他のオブジェクト指向言語では通常存在する、前後を囲こむクラス定義はありません。this キーワードを使用して、コンストラクター関数内にクラスのインスタンス メンバーに似たコードを定義します。コンストラクター関数の外側には、JavaScript メソッドをクラスにバインドする実際のプロトタイプ メソッドがあります。TypeScript のクラスを使用すると上記のサンプルと同様のコードを記述できますが、図 1に示すように、より自然な構文を使用して記述することもできます。

図 1 TypeScript クラス

class Animal
 {  
   name: string;
   species: string;
   habitat: string;
   constructor(name: string, species: string, habitat: string)
   {
     this.name = name;
     this.species = species;
     this.habitat = habitat;
   }
   sayhello()
   {
     Console.log("RAWR");
   }
 }

多くの開発者にとって、図 1 の TypeScript コード サンプルは、従来の JavaScript コードより読みやすくなっています。明らかに、コードがクラス定義やメンバーの一覧として機能し、引数の型が示されています。また、TypeScript では、型チェック、インターフェイス、コンパイル時の静的チェック、ラムダ スタイルの式、および通常 (インタープリター言語ではなく) コンパイル言語に存在する機能が提供されます。JavaScript 言語に対するこれらの拡張機能が有益なのは、一般的なコーディングの落とし穴に落ちなくて済むためです。

その他の一般的な JavaScript の問題は、JavaScript グローバル名前空間に、パブリック スコープが指定された変数があまりに多く存在しているために、グローバル名前空間が汚染される場合に発生します (これは開発者にとって日常茶飯事に思えます)。さいわい TypeScript には、名前空間のように動作するモジュールや、グローバル名前空間の汚染を防ぐクロージャを作り出すモジュールが実装されているので、この場合にも TypeScript は役に立ちます。TypeScript のモジュールには、内部モジュールと外部モジュールの 2 つの形式があります。内部モジュールには、現在のファイルで宣言しているコードが含まれています。外部モジュールは、現在のコード ファイルの先頭に /// を追加して、インポートする必要があります。module キーワードでモジュールを宣言でき、中かっこで閉じるだけでモジュールを終了できます。TypeScript モジュールは次のようになります。

module outerModule {
   "use strict";
   module innerModule {
     export function aFunction { s: string };
     export var variable = 1;
   }
 }

生成される JavaScript は次のようになります。

var outerModule;
 (function (outerModule) {
   "use strict";
   var innerModule;
   (function (innerModule) {
     function aFunction() { s: string }
     innerModule.aFunction = aFunction;
     innerModule.variable = 1;
   })(innerModule || (innerModule = {}));
 })(outerModule || (outerModule = {}));

このコードでは、Windows ストア アプリ内のどこからでもアクセスできるシングルトン モジュール インスタンスを outerModule 名前空間に作成しています。ご覧のように、モジュールは匿名関数 (つまり、即時実行関数式 (IIFE)) として表示されます。export ディレクティブが指定されているすべてのメンバーには、グローバル スコープが設定されています。これは internal キーワードが指定されている (つまり、プロジェクト全体を対象とする) C# メンバーに相当します。

TypeScript で Windows ストア アプリを構成して構築する

TypeScript と Visual Studio は密接に統合されていますが、TypeScript は別途提供されています。そのため、Visual Studio 2012 Express、Professional、または Ultimate Edition に加えて、次のツールをインストールする必要があります。

  • TypeScript (bit.ly/QHxbEZ、英語)
  • Visual Studio Web Essentials (bit.ly/MpJIHh、英語)

これらの拡張機能をインストールすると、[新しいプロジェクト] ダイアログ ボックスの [JavaScript] ノード直下に Visual Studio 向け TypeScript プロジェクト テンプレートが表示されます。この組み込みテンプレートは、適切な TypeScript アセットが組み込まれた HTML クライアント Web アプリ テンプレートです。したがって、このテンプレートは特に手を加えなくても機能します。

Visual Studio と TypeScript は密接に統合されていますが、この記事の執筆時点では、TypeScript を使用した組み込み Windows ストア プロジェクト テンプレートは存在しません (前述の HTML クライアント Web アプリ テンプレートだけが存在します)。ただし、TypeScript のドキュメントでは、近日公開予定とされています。それまでの間は、JavaScript を使用した Windows ストア アプリを構築する場合、空のプロジェクト テンプレート、グリッド プロジェクト テンプレート、分割プロジェクト テンプレートなど、現在の JavaScript 用プロジェクト テンプレートのいずれかを使用できます。TypeScript はこれらすべてのプロジェクト テンプレートで自動的に動作しますが、開発を進めるには、プロジェクトを少し変更する必要があります。

TypeScript を既存の Windows ストア アプリに統合するには、\tslib などのフォルダーに次の宣言ファイルをコピーする必要があります。

  • lib.d.ts
  • winjs.d.ts
  • winrt.d.ts

これらのファイルは TypeScript のダウンロード ページ (typescript.codeplex.com、英語) で入手できます。上記のファイルのファイル拡張子は末尾が .d.ts で、拡張子の d は declaration (宣言) を意味していることに注意してください。これらのファイルには、jQuery、ネイティブ Windows ランタイム (WinRT) ライブラリ、JavaScript 用 Windows (WinJS) ライブラリなど、よく使用されるフレームワーク向け型宣言が含まれています。図 2 は winjs.d.ts ファイルのサンプルで、一般的に使用される WinJS メソッドをまとめています。winjs.d.ts ファイルには、Visual Studio や他のツールでコンパイル時の静的チェックに使用されるパブリック宣言が多数含まれていることに注意してください。現時点では TypeScript はまだ未完成なので、不足している機能もあるでしょうが、そのような機能は自分で追加できます。

図 2 winjs.d.ts 定義ファイルの確認

declare module WinJS {
   export function strictProcessing(): void;
   export module Binding {
     export function as(data: any): any;
     export class List {
       constructor (data: any[]);
       public push(item: any): any;
       public indexOf(item: any): number;
       public splice(index: number, count: number, newelems: any[]): any[];
       public splice(index: number, count: number): any[];
       public splice(index: number): any[];
       public createFiltered(predicate: (x: any) => bool): List;
       public createGrouped(keySelector: (x: any) => any, dataSelector:
          (x: any) => any): List;
         public groups: any;
         public dataSource: any;
         public getAt: any;
       }
       export var optimizeBindingReferences: bool;
   }
   export module Namespace {
     export var define: any;
     export var defineWithParent: any;
   }
   export module Class {
     export function define(constructor: any, instanceMembers: any): any;
     export function derive(
       baseClass: any, constructor: any, instanceMembers: any): any;
     export function mix(constructor: any, mixin: any): any;
   }
   export function xhr(options: { type: string; url: string; user: string;
      password: string; headers: any; data: any;
      responseType: string; }): WinJS.Promise;
   export module Application {
     export interface IOHelper {
       exists(filename: string): bool;
       readText(fileName: string, def: string): WinJS.Promise;
       readText(fileName: string): WinJS.Promise;
       writeText(fileName: string, text: string): WinJS.Promise;
       remove(fileName: string): WinJS.Promise;
     }
 // More definitions

WinJS アプリを作成する場合、WinJS.Binding.List オブジェクトや WinJS.xhr オブジェクトなど、これらのメソッド スタブを詳しく知っている必要があります。WinRT/WinJS ライブラリのスタブはすべて自由に使用できます。これらの定義ファイルを使用すると、IntelliSense が Visual Studio で機能するようになります。

プロジェクトの任意のフォルダーに .ts ファイルを追加すると、対応する .js および .min.js (圧縮) 付属ファイルが Visual Studio で自動的に作成されます。Visual Studio で .ts ファイルを保存するたびに、TypeScript によってこれらのファイルが再構築されます。

ほとんどの Windows ストア JavaScript テンプレートでは、pages という名前のフォルダーに、各ページに必要な .html、.css、および .js ファイルの集合が格納されたサブフォルダーが含まれています。これらのファイルに加えて、data.js、default.js、navigator.js など、その他の JavaScript ファイルが \js フォルダーに格納されています。開発者は、これらの各ファイルに以下の手順を実行して、ファイルに TypeScript を統合する必要があります。

  1. 各ファイルの先頭に宣言の参照を追加します。たとえば /// を追加します。
  2. .js ファイルの名前を変更して拡張子を .ts にします。
  3. 既存の JavaScript を変更して、対応する TypeScript 言語構成要素 (モジュール、クラス、宣言など) にします。

たとえば、\js\data.js を統合するには、ファイルの先頭に参照を挿入し、トップレベルの関数を図 3のコードのようなモジュールに変換する必要があります。data.js ファイルの名前を data.ts ファイルに変更すると、Visual Studio によってファイルの保存時に、対応する .js ファイルとマッピング ファイルが作成されます。

図 3 data.js から data.ts への TypeScript の変換

// Original code in data.js
 (function () {
   "use strict";
   var list = new WinJS.Binding.List();
   var groupedItems = list.createGrouped(
     function groupKeySelector(item) { return item.group.key; },
     function groupDataSelector(item) { return item.group; }
   );
   // TODO: Replace the data with your real data
   // You can add data from asynchronous sources
   // whenever it becomes available
   generateSampleData().forEach(function (item) {
     list.push(item);
   });
   // ... More data-access code
 })();
 // The modified data.ts file
 ///
 ///
   module TypeScriptApp {
     "use strict";
     var list = new WinJS.Binding.List();
     var groupedItems = list.createGrouped(
       function groupKeySelector(item) { return item.group.key; },
       function groupDataSelector(item) { return item.group; }
   );
   // TODO: Replace the data with your real data.
   // You can add data from asynchronous sources whenever it becomes available
   generateSampleData().forEach(function (item) {
     list.push(item);
   });
   // ... More data-access code
 }

図 3 のコードは、Visual Studio ユーザーにはなじみ深い標準的な規則に準拠したプロジェクト名 (TypeScriptApp) を使用する、トップレベルの名前空間 (モジュール) として機能します。

もちろん、テンプレートに基づく現在の JavaScript を変更せず、そのままにすることもでき、その場合もコードは予想どおり動作しますが、形式や構文が一貫しないので保守が難しくなります。

Visual Studio の TypeScript 用オプション

TypeScript を最大限に活用するには、知っておく必要のある設定 (特に Windows ストア アプリの設定) があります。Windows ストア アプリでは圧縮 .js ファイルは必要ないので、Visual Studio の [ツール] メニューから表示できる [オプション] ダイアログ ボックスの [Web Essentials] タブで、[Minify generated JavaScript] (生成される JavaScript の圧縮) オプションを False に変更し、存在するすべての圧縮 .js ファイルを取り除くことができます。圧縮ファイルによってパフォーマンスが向上するのは、Web サイトだけです (クライアント アプリでは向上しません)。これは、圧縮 .js ファイルによって、必要なインターネット経由の帯域幅全体が削減されるためです。

Windows ストア アプリで TypeScript を使用するために必要なもう 1 つの変更は、[ツール] メニューから表示できる [オプション] ダイアログ ボックスで、エンコーディングを [Resave JS with UTF-8 BOM] (UTF-8 BOM で JS を再保存) に設定することです (ja.wikipedia.org/wiki/%E3%83%90%E3%82%A4%E3%83%88%E3%82%AA%E3%83%BC%E3%83%80%E3%83%BC%E3%83%9E%E3%83%BC%E3%82%AFを参照してください)。UTF-8 BOM (バイト オーダー マーク) を設定すると、起動時のアプリのパフォーマンスが向上し、Windows ストアのプロセス ライフサイクル管理ガイドラインに準拠するようになります (これらのガイドラインの詳細については、Windows 8 Special Issue における私のコラム「Windows ストア アプリのライフサイクル」(msdn.microsoft.com/magazine/jj660301) を参照してください)。パフォーマンスに加えて、UTF-8 BOM エンコーディング仕様も、Windows ストアの認定に合格して、自分のアプリを公開するために必要です。

JavaScript 開発者は、JavaScript ジェネレーターの配置済みコードやソース コードをデバックするにはソース マップなどのツールが不可欠なことをご存じでしょう。これは、ソース マップがコードを他のコードにマップするファイルであり、多くの場合は標準サイズの開発ファイルと、デバックが簡単ではない、圧縮されて結合された運用ファイルの間でマップするためです。Visual Studio のオプションで [Generate Source Map] (ソース マップの生成) を True に設定して、TypeScript と JavaScript の間でソース マップを作成し、TypeScript デバッグを有効にします。このオプションによって --sourcemap コンパイラ スイッチが適用される結果、ソース マップがコンパイル時に作成されます。

TypeScript コンパイラでは、既定で ECMAScript 3 (ES3) 互換コードにコンパイルされます。ただし、Visual Studio の [Compile to ECMAScript 3] (ECMAScript 3 にコンパイル) オプションを False に変更すると、ECMAScript 5 (ES5) コードを生成するように tsc コンパイラ フラグ --target が設定されるので、ES5 にコンパイルできます。プロパティ形式の構文、あるいはあらゆる ES5 または提案中の ES6 の TypeScript 機能を利用しようと考えている開発者は、このスイッチを設定する必要があります。

その他のメリット

JavaScript はすっかり普及し、かつてないほどその人気が高まっています。そのため、コンパイル言語の分野から JavaScript に移動する開発者は、TypeScript を利用して、アプリ規模の JavaScript コードを記述および管理できるようになりました。JavaScript 開発者は、そのままの JavaScript を記述する場合は通常利用できない、追加の型チェック サービスやコンパイラ サービスの恩恵を受けことができます。

Rachel Appel は、ニューヨーク市のマイクロソフトで開発者エバンジェリストを務めています。彼女に連絡を取るには、Web サイト (rachelappel.com、英語) にアクセスするか、rachel.appel@microsoft.com(英語のみ) 宛てにメールを送ってください。また、近況については Twitter (twitter.com/rachelappel、英語) をフォローしてください。

この記事のレビューに協力してくれた技術スタッフの Christopher Bennage (マイクロソフト) に心より感謝いたします。
Christopher Bennage (マイクロソフト)

Christopher Bennage はマイクロソフトの Patterns & Practices チームの開発者です。彼の仕事は、開発者が利用できるプラクティスを発見、収集し、推奨することです。最近彼の興味を引いているのが JavaScript と (カジュアル) ゲーム開発です。彼のブログは dev.bennage.com(英語) です。