AltJSとTypeScriptとは
昨今、
TypeScript以外にもさまざまなAltJSがあります。たとえばLL
TypeScriptの開発はC#やTurbo Pascalの開発者として有名なAnders Hejlsberg氏が中心となってMicrosoftで開発されました。
開発の中心はMicrosoftですが、
TypeScriptを今すぐに試してみる
TypeScriptのインストールについてはのちほど解説しますが、
TypeScriptの特徴とメリット
TypeScriptが実際にどのような言語なのか、
TypeScriptのおもな特徴は次のとおりです。
- 静的型付け言語であり、
あいまいさを回避しやすい - ECMAScriptのスーパーセットで、
かつECMA Script 6の機能を先取りしている - 変換しても平易なECMAScriptが出力される
- 既存ライブラリとの相互運用のために外部に型定義ファイルを持てる
TypeScriptの文法
TypeScriptの文法は基本的にJavaScriptの言語部分
function sayHello(message) {
alert(message);
}
sayHello("Konnichiwa!");このTypeScript のコードを変換すると次のJavaScriptのコードになります。
function sayHello(message) {
alert(message);
}
sayHello("Konnichiwa!");この2つのコードを比べるとわかりますが、
先ほどの変換例のとおり、
ではJavaScriptのコードをTypeScriptのコードにコピー&ペーストすればそのまま使えるかというと、
TypeScriptは静的型付け言語
TypeScriptの特徴は、
静的型付け言語とは、string、numberなどを宣言しておき、
function sayHello(message : string) {
alert(message);
}
sayHello("Konnichiwa!");この例では、sayHello関数は引数として文字列しか受け取れないことを示すため、: stringという宣言を追加しています。たとえば最後のsayHello関数の呼び出しで引数に数値の1を指定してコンパイルするとstring型を受け取るはずだけどnumber型が渡されていて、
var message: string; // string(文字列型)の変数を定義
message = 1;
// ↑ エラー: Cannot convert 'number' to 'string'.このように実行前のチェックが可能になるのが静的型付け言語のメリットです。静的な型付けをするメリットはのちほどもう少し詳しく紹介します。なお、
大規模開発をサポートする要素
TypeScriptには、
module Sample {
export class Greeter {
message: string;
constructor(message: string) {
this.message = message;
}
sayHelloAfterSecond(): void {
setTimeout(() => alert(this.message), 1000);
}
}
}
new Sample.Greeter('Konnichiwa!!').
sayHelloAfterSecond();
モジュールを利用すると、
静的型付け言語であるメリット
TypeScriptが静的型付け言語であり、
コンパイル時のエラー
先ほど述べたとおり、
これはリファクタリングなどを行う場合にもメリットになります。たとえば特定のクラスのメソッド名を変更した場合、
Visual StudioやWebStormなどのIDEとの相性の良さ
Visual StudioやWebStormといったいわゆるIDE
IDEのサポートで代表的なものと言えばコード補完ではないでしょうか。Visual StudioやWebStormでも、
またコードのエラーをエディタ上に表示する機能もあります。常にコードの状態を確認できるので、
IDEらしい強力な機能としては、
このような機能は静的な型付けをしているおかげで正確に動作できているのです。Visual StudioやWebStormにはJavaScriptのコード補完のサポートもありますが、
ハイライトやリネームの機能はPlaygroundでも実際に体験できますので、
TypeScriptのインストールとコンパイル
Playgroundでコードを書くのは即座に試せてよいのですが、
インストールする
Windowsをお使いの方はVisual Studioを利用するのが簡単でしょう。Visual Studio 2013のバージョンUpdate 2以降にはTypeScriptの開発ツール
またOS XやLinux、sudoを付けて次のコマンドを実行してください。
% npm install -g typescriptこのコマンドを実行してインストールが完了すると、tscコマンドを実行できるようになります。
コードを書いてコンパイルする
Visual Studioでは、
Node..ts)tscコマンドの引数として渡せばコンパイルされます。
% tsc script.tstscコマンドを実行すると、.tsファイルと同名の.jsファイルが生成されます。しかし、tscコマンドを実行するのは面倒です。そのため、tscコマンドには変更を監視して自動でコンパイルする--watchオプションが用意されています。
% tsc --watch script.ts--watchオプションを指定してtscコマンドを実行すると終了せずにそのまま待機状態になり、
TypeScriptの文法
JavaScriptにも型がありますが、
型と変数の宣言
TypeScriptは組み込みで次の型を持っています。
- string:文字列型
- number:数値型
- boolean:ブール値
(true/ false) 型 - void:何もない、
空からを表す型
ほかにも関数やオブジェクト、nullなどの型もありますが、
TypeScriptでの変数宣言はJavaScriptと同様varを使います。宣言の変数名の後ろにコロンと型名を書くと変数の型を指定できます。
var text: string;
var count: number;
var enabled: boolean;変数の型が指定されている場合、
// 文字列型の変数に数値を代入しようとしてエラー
text = 1;
// 数値型の変数に文字列を代入しようとしてエラー
count = "1";
// ブール型の変数に数値を代入しようとしてエラー
enabled = 0;しかし、
var text = "hauhau";
var count = 10;
var enabled = true;一見JavaScriptと同様に見えますね。TypeScriptでは型推論というしくみにより、
// 文字列型の変数として初期値を持たせつつ定義
var text = "hauhau";
// 文字列型の変数に数値を代入しようとしてエラー
text = 1;これでまったく異なる型の値を間違えて代入することや混在することがなくなります。気軽に記述しながら静的型付け言語の恩恵を受けられるのです。
関数の定義
TypeScriptでの関数の定義は、functionキーワードに続けて関数名を書き、
// 引数aとbはnumber型で、戻り値がstring型
function addAndToString(a: number, b: number): string {
return (a + b).toString();
}
addAndToString('1', '2'); // エラー: パラメータはnumber型しか受け取らない
var value: number = addAndToString(1, 2); // エラー: number型の型にstring型は入ない
JavaScriptには通常の関数定義のほかに匿名関数
document.body.addEventListener('click', function () {
alert('clicked!');
});TypeScriptでは、
document.body.addEventListener('click', () => {
alert('clicked!');
});長かったfunctionキーワードがなくなり、=>が登場しました。functionがなくなったおかげでスッキリしています。
さらに、
[1,2,3].map((v) => { return v * 2; });
[1,2,3].map((v) => v * 2);ブロックを取る書き方はfunctionを使う定義とほとんど変わりませんが、
[1, 2, 3].map(function (v) {
return v * 2;
});なお、
[1,2,3].map(v => { return v * 2; });
[1,2,3].map(v => v * 2);クラスの定義
TypeScriptでクラスを定義するには、
// Greeterクラスを定義
class Greeter {
// string型のプロパティ
message: string;
// パラメータを取るコンストラクタ
constructor(message: string) {
this.message = message;
}
// メソッド
sayHello(): void {
alert('Hello! ' + this.message);
}
}
new Greeter('Konnichiwa').sayHello();クラスの定義にはプロパティ、functionキーワードは使いません。
定義したクラスを使うにはJavaScriptとまったく同様に、newでインスタンスを生成して利用します。これはJavaScriptから直接扱えるという意味でもあり、
TypeScriptではメソッドやプロパティにprivate修飾子を付けて宣言すると、
ほかのクラスベースの言語と同じように、Greeterクラスを継承したAisatsuクラスを定義して、sayHelloメソッドを上書きしています。
class Aisatsu extends Greeter {
sayHello(): void {
alert('Konnichiwa! ' + this.message);
}
}ところでコードを書いていると、
// canvas要素を取ってくる
var canvasE = document.querySelector('canvas');
// Canvas 2D Contextを取得する
var ctx = canvasE.getContext('2d');このコードはJavaScriptとしては問題なく動作しますが、
まず、document.はドキュメント内のcanvas要素を返しますが、querySelectorメソッドの戻り値は要素を表す汎用的なElement型ですcanvasEはElement型です)。この値に対してgetContextメソッドを呼び出そうとしても、Element型にはそのようなメソッドはないのでコンパイルエラーになります。
この場合は型のキャストElement型ではなくgetContextメソッドを持つ下位のHTMLCanvasElement型であると認識できればよいのです。キャストするには、<TypeName>を変数や戻り値の前に置きます。このコードでは、document.の戻り値をHTMLCanvasElement型にキャストしています。
// canvas要素を取ってくる
var canvasE = <HTMLCanvasElement>document.querySelector('canvas');
// Canvas 2D Contextを取得する
var ctx = canvasE.getContext('2d'); // ← OK
キャストのエラーは最終的に実行されたときに発生するので注意が必要です。リスト3のコードでは、canvas要素の実体がまさにHTMLCanvasElement型なので正常に動作しますが、div要素など)HTMLCanvasElement型にキャストすると、
モジュール
TypeScriptにはモジュールと呼ばれるしくみがあります。モジュールには外部モジュールと内部モジュールがありますが、Greeterというクラスが2つ出てきますが、
module Sample.Module1 {
export class Greeter {
hello(): void { alert('Hello!'); }
}
}
module Sample.Module2 {
export class Greeter {
hello(): void { alert('Konnichiwa!'); }
}
}
new Sample.Module1.Greeter().hello(); // => Hello!
new Sample.Module2.Greeter().hello(); // => Konnichiwa!
モジュールを定義するにはmodule ModuleName {…… }というブロックでモジュールに含めたいコードを囲みます。デフォルトではモジュール内で定義されたクラスはそのモジュールで閉じていて、classなどの定義の前にexport修飾子を指定する必要があります。
module Sample.Module3 {
class Greeter {
hello(): void { alert('Hi!'); }
}
}
new Sample.Module3.Greeter().hello();
// => classをexportしていないのでコンパイルエラーインタフェース
TypeScriptにはインタフェースと呼ばれるしくみがあります。インタフェースは、
たとえば、messageプロパティを持つインタフェースを指定したクラスを定義しています。
interface IGreeting {
message: string;
}
class Hello implements IGreeting {
message = 'Hello!';
}
class Konnichiwa implements IGreeting {
message = 'Konnichiwa!';
}
function say(greeting: IGreeting): void {
alert(greeting.message);
}
say(new Hello()); // => Hello!
say(new Konnichiwa()); // => Konnichiwa!インタフェースはクラスの宣言の一部として指定でき、class Hello implements IGreetingという宣言は、HelloはIGreetingインタフェースのメソッドを持つ必要がある」Helloクラスからmessageプロパティを削除するとコンパイルエラーになります。
代入の互換性
TypeScriptのインタフェースやクラスは、
interface IGreeting {
message: string;
}
function say(greeting: IGreeting): void {
alert(greeting.message);
}
// IGreetingと同じプロパティを持つがIGreetingの実装を明示的に宣言していない
class Hi {
message = 'Hi!';
}
// HiクラスはIGreetingとして扱える
say(new Hi()); // => Hi!
// 匿名オブジェクトはstring型のmessageプロパティがあるのでIGreetingとして扱える
say({ message: 'Ohayo-gojyaimasu!' }); // => Ohayo-gojyaimasu!
一方、messageプロパティの型がstringではないのでエラーになり、messageプロパティがないのでエラーになります。
say({ message: 1 });
say({ msg: 'Hoge!' });その他の機能
TypeScriptには、
- ジェネリクス
- プロパティ
(getter/ setter) - 列挙型
(Enum) - インデクサ
- import対応
機能は多いですが、
外部ライブラリと定義
ここまでTypeScript自体の紹介をしてきましたが、
TypeScriptを利用した開発で使うライブラリは、
そこでTypeScriptには、
しかし、npmパッケージからtsdコマンドをインストールすれば簡単に取得/
まとめ
ほんの触り程度の紹介しかできませんでしたが、
