TypeScript をはじめる時のまとめ
2014.11.12
この記事は最終更新日から1年以上が経過しています。
TypeScriptで実装を行って早半年以上が過ぎてしまいました。。。
自由奔放にJavaScriptを書いていましたが、すっかり縛られたい体となってしまい(変な方向だな。)TypeScropt無しではもう生きられないのか? てぐらい。(いや別に無しでも大丈夫だな。)
とりあえず、今からTypeScriptをはじめる!って方もいるかと思いますので、今回はTypeScripeを一通りまとめてみることにしました。
少しでも参考になればと思います。
TypeScriptとは
まず、TypeScriptとはなんぞやってところからですが、
TypeScriptはマイクロソフトによって開発されたフリーでオープンソースのプログラミング言語で、「CoffeeScript」や「Haxe」など Altjsと言われる種類で扱われております。
Altjsと呼ばれるものは、そのまま使うのではなくコンパイルを行うことによってJavascriptに変換できる言語で、JavaScriptの言語仕様が難しく、柔軟すぎるが故に発生する問題を解決することに用いられる言語となっております。
TypeScriptの大きな特徴として、「静的型付け」「クラスベースオブジェクト指向」を取り入れた言語となっております。
ま、まずは使ってみようではありませんか、と言うことで、ブラウザ上でテストが行えるTypeScript Playgroundなるものがありますので、触ってみましょう。
TypeScript Playground
TypeScript Playground
http://www.typescriptlang.org/Playground
画面を開くと 左側が「TypeScript」 右がコンパイル後の「JavaScript」となっております。
まず最初に class Greeter { 〜〜 とTypeScriptで記述されていて、コンパイル後のJavaScriptでは var Greeter = (function() { 〜〜 となっているのが確認できます。
JavaScriptでは 「Class」と概念はないのですが、このように即時関数でカプセル化を行うことによってClassぽく扱うことが出来ます。
こういったJavaScript独自の記述で可読性もあまり良くないのですが、TypeScriptを用いることによって素直に美しくクラスによるカプセル化を実現し、問題を解決することが可能となっております。
また、greeting: string; のような型指定、constructor(message: string) { 〜のようにコンストラクタの宣言もTypeScriptの特徴と言えます。
セレクトを Modulesに切り替えると表示するのですが、
module Sayings { 〜のようにmoduleも使用することができます。
このようにオブジェクト指向プログラミングの核となる、
- クラス
- インターフェイス
- モジュール
等を使用できるのが、TypeScriptの特徴でもあります。
では、そんなTypeScriptのポイントを掻い摘んでまとめてみます。
インストール
まずはインストール。
先ほどのTypeScript Playgroundは、確認用として使っていただければと。
実際には作業されるPCのローカルにインストールして利用していきます。
Node.jsに付属するパッケージマネージャー「npm」を使えば簡単にインストールできます。
npm install -g typescript
TypeScriptがインストールされるとコンパイラである「tsc」コマンドが使用できます。
早速、TypeScripeファイルを作成してみましょう。
hello.ts
class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; } } var greeter = new Greeter("world");console.log(greeter.greet());
TypeScripeファイルは.tsの拡張子を持ったファイルとなります。
作成したら「tsc」コマンドで
tsc hello.ts
すると、hello.jsがコンパイルされますのでnodeで実行。
node hello.js
すると、「Hello, world」と返ってくるかと思います。さぁ始まりです!
変数
TypeScriptでの変数の定義は「var 変数名: 型名;」となります。
var v: any; v = 1; var n: number = 1;
anyは何でも受け入れるため、JavaScript同様、数字でも文字列でも代入が可能となっております。
定義した型名と異なる型を代入しようとすると、コンパイルエラーとなりコンパイルは行われません。
var n: number; n = 'webcyou';
上記の件は、コンパイルエラーとなります。
型
any
any型は数字でも文字列でも代入が可能となっていて、何でも受け入れる型となっております。
JavaSCriptは動的型付けの言語なので、nullとundefinedを除き、変数が参照しているオブジェクトがどんなオブジェクトかに関わらず、どんなプロパティにアクセスしてもエラーとならず、存在しないプロパティを読み取ろうとしても、単にundefinedと未定義の値が返ってくるだけです。
このような動的型付け言語の特有の振る舞いを防ぐのが静的型付けのメリットですが、JavaScriptのスーパーセットとして用意されたTypeScriptもJavaScriptと同じように変数が扱えなければならないため、「any」が用意されております。
プリミティブ(組み込み)型
TypeScriptでは、プリミティブ型としてNumber, Boolean, String, Void, Null, Undefinedが用意されています。
これらの宣言は小文字を使って宣言を行います。
var b: boolean = true; var s: string = "webcyou"; var n: number = 1;
void
voidは「ない」ことを宣言する型となっていて、nullとundefinedのみがvoidの値として許容されます。
//表示の目的で戻り値がない場合の宣言
function hello(hoge: string): void { console.log("webcyou" + hoge); }
Object
Objectはすべてのオブジェクトのベースとなります。
また、Objectは関数toStringを常に持っております。
var objA: object = true; var objB: object = 1; var objC: object = "string"; console.log(objA.toString()); //文字列としてtrueが返る
nullとundefined
nullとundefinedの値の型は「any」となるので、全ての型の変数に代入が可能です。
var b: boolean = null; var s: string = null; var n: number = null;
変数の型注釈
変数の型は、型注釈(Type Annotation)でソースコード中で明示的に宣言できます。
変数名の直後にコロンと型を指定します。
var name: string = "takayama";
省略も可能でその場合は型推論(Type Inference)により変数の型が決定されます。
配列
TypeScriptでは配列の要素の型は静的に決定され、その型の値しか配列に含めることができません。
例:配列の要素全てstringの場合。
var colors: string[] = ["black", "white", "yellow", "red"];
配列に関しても型推論が有効で、上記で型省略した場合は自動的にstring[]であることを推論します。
numberやstringの混合された場合は、何もプロパティの持たないオブジェクトの配列({}[]型)となります。
{}[]型としての推論
var hoge = ["webcyou", 99, true];
オブジェクトリテラル
var obj = {};
JavaScriptは動的型付けのため、オブジェクトに後からプロパティを追加することが可能ですが、TypeScriptではコンパイルエラーとなります。
var obj = { name: "takayama" }; obj.age = 36; //ageプロパティを追加 コンパイルエラー console.log(obj.name, obj.age);
関数定義
関数とTypeScriptの静的型付け
TypeScriptは静的型付け言語ですので、関数定義に型注釈を与えることが可能です。
function hello(word: string): string { return "hello," + word; } var result: string = hello("world!"); console.log(result);
この場合、引数にstring型、関数、戻り値にもstring型を定義していますので、number型が返ってきた場合コンパイルエラーを返すことになります。
アロー関数式
アロー関数式(Arrow Function Expressions)は無名関数の構文で、シンプルに記述することができます。
ECMAScript 6で定義されているJavaScriptの新しい書き方になります。
TypeScript
var hello = (word: string): string => { return "Hello!" + word; };
JavaScript
var hello = function() { return "Hello!" + word; }
(x: number) => { return Math.sin(x); }
実行内容が1文の場合は{}とreturnも省略できます。
(x: number) => Math.sin(x);
引数が1つ且つ、型指定もない場合()も省略可能
x => { return Math.sin(x); }
{}とreturnを省略
x => Math.sin(x);
また、引数の個数、それぞれの型と戻り値の型をあらかじめ決めておくことも可能です。
引数としてstringを取り、stringを返す関数となります。
TypeScript
var hello: (word: string) => string; hello = word => "Hello," + word;
JavaScript
var hello; hello = function (word) { return "Hello," + word; };
省略可能引数
省略可能の引数の指定は「?」を追加することによって行えます。
var hello = (word?: string) => { if (word === undefined) { word = "world"; } return "Hello," + word; } hello(); //Hello, world hello("webcyou"); //Hello, webcyou
デフォルト値付き引数
また、引数にデフォルトの値を付与する場合は「=」を使って付与します。
TypeScript
var hello = (word = "world") => "Hello," + word; hello(); hello("webcyou");
JavaScript
var hello = function (word) { if (typeof word === "undefined") { word = "world"; } return "Hello," + word; }; hello(); hello("webcyou");
可変長引数
引数の数を呼び出し側から自由に増減させる引数の場合、「…」で可変長引数を用いて実装できます。
TypeScript
var hello = (...words: string[]) => { return "Hello," + words.join(","); } var result = hello("webcyou", "takayama"); console.log(result);
JavaScript
var hello = function () { var words = []; for (var _i = 0; _i < (arguments.length - 0); _i++) { words[_i] = arguments[_i + 0]; } return "Hello," + words.join(","); }; var result = hello("webcyou", "takayama"); console.log(result);
クラス
と、やっと本題に入る気がします。。
JavaScriptは簡単に色んなスタイルで自由に書けるが故に、様々な書き方や流儀が存在してしまい技術的負債を量産してしまいがちです。
そこから生まれるソースを理解するコストや混乱、また少し修正を行おうとすると、バグが発生したりと様々な問題が発生しがちですが、それらを解決する手段の1つとして、TypeScriptにクラスは用意されております。
定義
クラスを定義するには「class」の後にクラス名を記述します。
class Greeter { greeting: string; } var greeter = new Greeter();
クラスを使う際は「new」演算子を使い、「new クラス名();」と記述することでクラスの新しいインスタンスを得ることができます。
継承
クラスの継承は「extends」を用いて継承を行います。
class Base { name: string; hello() { return "Hello," + this.name; } } class Child extends Base { constructor(name: string) { super(); this.name = name; } howAreYou() { return this.hello() + ". How are you?"; } } var result = new Child("takayama").howAreYou(); console.log(result);
アクセス修飾子
クラスの変数やメソッドなどの要素にアクセス修飾子を付与することができます。
- public: クラスの外部に公開して、外部からのアクセスを許可する。
- private: クラス内部からのアクセスだけを許可し、外部には非公開。
class Sample { private str1 = "yeah!!!"; public str2 = "webcyou"; private hello(word = "world") { return "Hello!" + word; } public hellowith(word = "world") { return this.hello(word) + "!"; } public yeah(word = "world") { return this.str1 + "," + word; } } var sample = new Sample(); var s1 = sample.str1; //エラー var s2 = sample.str2; var s3 = sample.hello() ; //エラー var s4 = sample.hellowith(); var s5 = sample.yeah();
コンストラクタ
コンストラクタは「constructor」で定義します。
主にインスタンス変数をコンストラクタで初期化を行う際に利用します。
class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; } }
コンストラクタの引数に「public」や「private」の修飾子を付与することも可能です。
constructor(public message: string) { 〜〜
constructor(private message: string) {〜〜
class Greeter { message: string; constructor(message: string) { this.message = message; } }
↓
class Greeter { constructor(public message: string) { } }
「get」「 set」
「get」「set」アクセサを定義できます。
class Sample { private _secretString: string; get str(): string { return this._secretString; } set str (value: string) { this._secretString = value; } }
オーバーライド
メソッドのオーバーライドも可能です。
class Base { greeting() { return "Hello"; } } class Childs extends Base { greeting() { return "Hey!"; } } var obj = new Childs(); console.log(obj.greeting());
superキーワード
「super」キーワードを使えば子クラスから親クラスのpublicなメソッドにアクセスできます。
class Base { constructor() { console.log("BaseClass"); } method() { return "Base"; } } class Childs extends Base { constructor() { super(); console.log("ChildClass"); } method() { return "Child"; } test() { return super.method(); } } var obj = new Childs(); console.log(obj.test());
console.log
BaseClass ChildClass Base
スタティックメンバ
「static」を付与したメンバはスタティックなメンバとなります。
class Greeter { static hello(word = "world") { return "Hello," + word; } hello(word = "webcyou!") { return "Hello," + word; } } var a = Greeter.hello() ; var b = new Greeter().hello(); console.log(a, b);
console.log
Hello,world Hello,webcyou!
aはクラスに紐づくスタティックメソッドを利用しています。
bはインスタンスのメソッドとなります。
インターフェース
インターフェースは実装を持たない定義の塊となります。
interface Data { num: number; score: number; text: string; method(): string; } class PrivateData implements Data { constructor( public num: number, public score: number, public text: string ) { } method() { return "num:" + this.num + ", score:" + this.score + " " + this.text; } } var a = new PrivateData(1,100,"獲得!"); console.log(a.method());
console.log
num:1, score:100 獲得!
インターフェイスの継承
インターフェイスもextendsを用いて継承することが可能です。
interface AllMessage extends Message { contents: any; }
モジュール
内部モジュール(Internal Module)はTypeScript自身に用意されたモジュール機能で、TypeScriptのコードは内部モジュールにより分割された名前空間が提供されます。
module Sample { var value = "webcyou"; function hoge(arg: string): void { console.log("hoge"); } class Webcyou { method(){} } module NestSample { var hoge = "hoge"; } }
クラスやインターフェースやネストしたモジュールも定義できます。
モジュールメンバのエクスポート
外部参照を行いたい際は「export」キーワードでエクスポートさせ参照を行うことが可能です。
module MyApp { class NotExported { } export class Exported { } export module InnerModule { export class Exported { } } } var a = new MyApp.NotExported(); //エラー var a = new MyApp.Exported(); //OK var a = new MyApp.InnerModule.Exported(); //OK
総称型
総称型(Generic Types)は、クラスや関数の型定義の一部をパラメータ化することで1つの定義で様々な用途に対応できるようにするための方法です。
class DataContainer<Type> { data: Type; } var a = new DataContainer<string>() ; a.data = "data"; //OK var b = new DataContainer<boolean>() ; b.data = true;//OK
リファレンスコメント
tsファイルから他のソースファイルのモジュールや関数等を参照する場合、リファレンスコメントを用いることで可能となります。
/// <reference path="hello.ts" />
d.tsファイル
/// <reference path="../../../angularjs/angular.d.ts" />
d.tsファイルは型定義ファイルとなります。
メジャーな定義ファイルはこちらでダウンロードできます。
http://definitelytyped.org/tsd/
npm を用いてインストール
npm install tsd -g
gruntで使用
とざっと、TypeScriptに関してまとめてきました。
で、実際どう使うのかといいますと、毎度毎度コンパイルをするわけでもなくgrunt-typescript等がありますので、こちらを使ってwatchでコンパイルさせる方法が最もベターな方法でしょう。
https://www.npmjs.org/package/grunt-typescript
npm install grunt-typescript
次回はもっと詳細なところを書いていければと思います。
ではでは。