このサイトは、只今WEB業界で活躍中のデザイナー、プログラマーの方々の情報を集めたweb統合情報サイトです。

web帳

記事詳細

2014.11.12

TypeScript をはじめる時のまとめ

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: クラス内部からのアクセスだけを許可し、外部には非公開。
省略の場合は 「public」となります。
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

次回はもっと詳細なところを書いていければと思います。
ではでは。

  • RSSを登録する

  • follow us in feedly

Graphical FrontEnd Engineer
- Daisuke Takayama

MAD CITY 北九州市で生まれ育つ。20代はバンド活動に明け暮れ、ふと「webデザイナーになりたい。」と思い、デジタルハリウッド福岡校入学。卒業後、数々の賞を受賞、web業界をざわつかせる。
現在、港区六本木で活動中。

WEBデザイナーの、WEBデザイナーによる、WEBデザイナーの為のサイト。「みんなで書こう!」と仲間を募ってみたが、結局書くのは自分だけとなってしまいました。日々のメモを綴っていきます。