読者です 読者をやめる 読者になる 読者になる

yutaponのブログ

javascript界隈の興味あるネタを備忘録的に残しておく場所

TypeScript触ってみた感想

TypeScriptリファレンスを買って、4章まで読んだのでその感想を。
大雑把な感想としては、勉強しておいて損はない言語、といったところです。

ちなみに章構成はこんな感じです。

  1. 概要
  2. 開発環境の構築
  3. TypeScript & JavaScript基本文法
  4. 基礎知識
  5. 必要とされるJavaScript知識
  6. 応用知識
  7. 開発支援ツール
  8. 開発サンプル

TypeScriptリファレンス Ver.1.0対応

TypeScriptリファレンス Ver.1.0対応

 

TypeScriptの特徴

TypeScriptはAltJSのうちの一つってくらいの予備知識で読み始めたので、1章は多くの学びがありました。

TypeScriptはJavaScriptのスーパーセット

TypeScriptはES6の機能を取り込んで、型システムを取り入れた言語みたい。

ES6を取り込んでるってのが結構良くて、現状まだまだES6を実行できる環境が整っていないけれど、TypeScriptを使っていれば新しい記法とかに慣れておくことが出来る。

静的型付け言語

静的型付けの利点はコンパイル時にエラーを検出できたり、IDEとか利用時にコード補完の恩恵を受けることが出来る。

生のJSを書いていると、メソッドの引数の型を仮引数名だけで表現するのは限界があるのでコメントで表現するけれど、それはプログラマへ向けたコメントであって、処理系へ伝えることはできない。

TypeScriptで書くなら

function hello(word: string): void {
    console.log('hello, ' + word);
}

という感じで引数の型と戻り値を定義することができ、
かつ与えられた引数の型が定義されたものと違ったらコンパイラがエラーを吐いてくれるので、だいぶ精神的に良い。

他のAltJSとの違い

JSXとの比較が述べられていて面白かったです。

JSXで書いたソースはコンパイル後にはガチガチに最適化され、元のソースとはかけ離れたコードになるようです。

一方TypeScriptはというと、ソースとコンパイル後のコードを1対1になるように出力します。

JSXはパフォーマンスを重要視しているのに対して、TypeScriptはデバッグのしやすさも考慮している?ってことでしょうか。
(SourceMapでデバッグのしやすさは均衡しますし、TypeScriptのコンパイラもゆくゆくは最適化に取り組んでいくとは思います)

TypeScriptの開発環境を整える

WindowsならVisualStadio一択かと思いますが、OSX, Linuxはいろいろと選択肢があります。

私は普段SublimeText2を使っているのでこちらに環境を構築しました。

コンパイルできるようにする

まずはTypeScriptをコンパイルするためのモジュールをnpmからインストールします。

$ npm install -g typescript
...
# tscコマンドが使えるようになる

コンパイル出来るようになったので、エディタ側の設定をします。

Sublimeの設定をする

Sublimeのパッケージマネージャーから「TypeScript」というパッケージをインストールします。
これを入れるとSyntax Highlightが効くようになります。

次にCmd+Bでビルドが走るように設定しておきます。

こちらを参考に作業しました。
Sublime Text 2 で TypeScript をビルドする - Y.A.M の 雑記帳
(2012年の記事なのがすごい)

若干違うけど、私はこんな感じで設定しました。

TypeScript.sublime-build

{
    "cmd": ["tsc", "--sourcemap", "$file"],
    "file_regex": "^(.+?) \\((\\d+),(\\d+)\\)(: .+)$",
    "line_regex": "\\((\\d+),(\\d+)\\)",
    "selector": "source.ts",
    "osx": {
       "path": "/usr/local/bin:/opt/local/bin:/Users/userName/.nodebrew/current/bin"
    }
}

nodebrewでnode.jsのバージョン管理をしていたので、$ which tscした結果をpathに追記しました。

.tsファイルを開きながらCmd+Bすると、同じ階層に.jsファイルと.js.mapファイルが生成されます。

とりあえずこれでTypeScriptを試す環境は整いました。

試しにサンプルコードをhello.tsというファイルに保存してコンパイルしてみます。

module Greeting {
    export class Hello {
        constructor(private text: string) {
        }

        say(): void {
            console.log(this.text);
        }
    }
}

var hello: Greeting.Hello = new Greeting.Hello("Hello, world!");
hello.say();

 

これをコンパイルすると、hello.jsが出力されます。

var Greeting;
(function (Greeting) {
    var Hello = (function () {
        function Hello(text) {
            this.text = text;
        }
        Hello.prototype.say = function () {
            console.log(this.text);
        };
        return Hello;
    })();
    Greeting.Hello = Hello;
})(Greeting || (Greeting = {}));

var hello = new Greeting.Hello("Hello, world!");
hello.say();
//# sourceMappingURL=hello.js.map

だいぶ読みやすいコードが出力されました。

それで実行するときはブラウザでscriptタグで読み込むか、nodeで実行します。

手っ取り早く確認したいので、nodeで確認しました。

$ node hello.js
Hello, world!

フロントでもバックエンドでもTypeScriptは使える

tscコンパイルオプションに「--module commonjs」または「--module amd」を指定するとそれぞれの形式でモジュール化された.jsファイルが出力されます。

フロントでRequireJSを使っていればamd形式で出力して、Browserifyを使っていればcommonjs形式で出力すれば良さそうです。

またcommonjs形式ならNode.jsアプリケーションもTypeScriptを使って開発することができます。

サーバーサイドJSでこそ、TypeScriptが主流になっていきそうな気がしますね。

class, extends, interface

4章の内容を全部紹介するには量が多いので、オブジェクト指向の代表的キーワードだけご紹介を。

適当にclass, extends, interfaceを使ったプログラムを書いてみました。

// ヒューマンインターフェースを定義
interface Human {
    name: string;
    age: number;
    greeting(): string;
}

// 日本人クラス
class Japanese implements Human {
    constructor(public name: string, public age: number) {
    }

    greeting(): string {
        return 'こんにちは!';
    }
}

// 西郷クラス
class Saigo extends Japanese {
    public name: string;
    public age: number;

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

    greeting(): string {
        return 'ごわす!';
    }
}

// インスタンス生成
var japanese = new Japanese('一郎', 10);
var saigo    = new Saigo('西郷隆盛', 30);

// greetingメソッドを実行
console.log(japanese.greeting()); // => こんにちは!
console.log(saigo.greeting());    // => ごわす!

ヒューマンインターフェースを定義して、名前と年齢とあいさつメソッドを定義しています。

日本人クラスはヒューマンインターフェースを実装しています。
この調子でアメリカ人クラスとか作ってもいいかもしれません。

また日本人クラスを継承して西郷どんクラスを作りました。

最後に2つのクラスのあいさつメソッドを実行しています。

こんな簡単なクラスを実行するため、何回もコンパイラに怒られました。
でもそれがいいんです。
コンパイラが実行する前に不備があることを教えてくれます。

ちょっと上のコードを解説してみます。

interfaceについて

JavaScriptでインターフェースが使えるってのが魅力大きいですよね。

interface Human {
    name: string;
    age: number;
    greeting(): string;
}

interfaceは値と振る舞いの定義だけして、実装はしません。
定義の仕方は variable: type; または funcName(): type; といった形です。
値と値の型、メソッド名と戻り値の型をコロンで区切って記述し、最後にセミコロンを付けます。

また、TypeScriptでは private または public のアクセス修飾子を付けることができるのですが、interfaceで定義したプロパティやメソッドはpublicでアクセスできなければいけません。

classのconstructorについて

constructorはクラスのインスタンスを生成する際に実行される特別なメソッドです。

Japaneseクラスの方を少し解説します。

class Japanese {
    constructor(public name: string, public age: number) {
    }
}

constructorの仮引数の部分で、public name: string, public age: numberと定義していますが、これだけで

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

と同じことをしたことになります。
短く書けてすっきりしますが、知らないと少し混乱するかもしれません。

extendsについて

JavaScriptでもextendsは表現しやすい機能です。

Saigoクラスのconstructorを見てみます。

    constructor(name: string, age:number) {
        super(name, age);  // これ呼ばないとコンパイルエラー
        this.name = name;
        this.age = age;
    }

継承したクラスでは、コンストラクタで親のコンストラクタを呼ばないとエラーになるようです。
また親のJapaneseクラスのコンストラクタは引数を2つ取るので、
super(name, age) と2つの引数を渡しています。

superキーワード

superは子クラスから親クラスのpublicなメソッドにアクセスするときに使います。

単にsuper()と書いた時はコンストラクタを呼び出すという意味になります。

Saigoクラスから親クラス(Japaneseクラス)のgreeting()を呼びたい場合は
super.greeting()と記述すればよい訳です。

TypeScriptの所感

普段JavaScriptを一番使っている人の感想からすると、TypeScriptは書いてて気持ちが良いですね。

なんというか、テストコードに守られてリファクタリングしているときと似たような感じがします。
TypeScriptのコンパイラがテストコード的な立ち位置で、
整合性のとれていないコードを書いたらエラーを吐いてくれるので。

ちょっと触っただけですが、だいぶ好きになりました。 最初はちょっとお固いイメージがあったのですが、
JSのスーパーセットなのでJSの知識がほぼ活かせます。

まだ4章までしか読んでないので引き続き読み進めようと思います。

おわりに

他にもstatic(クラス変数)とか、Enumとか、Moduleとか、ジェネリクスとかいろいろあるんですが今回はここまで。

あまりがっちりオブジェクト指向で設計をした経験ないので、そっちの設計力も鍛えておきたい。

どうでもいいけど、今回はMarkdownで書いてみた。
プレビューしながら書けるの超便利。
不満があるのは章立てのスタイルが効いていない件。
マージンが詰まりすぎていて全体的に読みにくくなってるので
来週にははてな記法に戻っているかも。