あなたは JavaScript 、できますか?
「できなくはないけど、やりやすくは感じないなぁ」
って人、多いんじゃないかと思います。

そんな人にオススメしたいのが TypeScript です。非常に便利なので、ぜひ使ってみてほしいです。
今回は TypeScript を触っていてこう書いたらいいかな〜?こう書いてほしいなぁ なんて思ったりしたものをあげてみたいと思います。

import export を活用する

TypeScript の解説でよく

/// <reference path="hoge.ts" />

というものがありますが、 import export の構文を使うのがオススメです。
使われる側のコードでは

class Hoge {
    /*
    実装
    */
}
export default Hoge;

使う側のコードでは

import Hoge from './path/to/hoge';
new Hote();

のような形となります。この方が何を読み込んだのかわかりやすいです。
基本的に実装する機能と export するコードをソースファイル単位で対応させるのが良いです。

import export を集約する

コーディングしていてクラス定義が増えてくるとライブラリのようにしたくなってきます。
そんな時は以下のようにまとめて export するインデックスファイルを作りましょう。

import AAA from './aaa';
import BBB from './bbb';
import CCC from './ccc';
export {
  AAA,
  BBB,
  CCC,
};

使う側では

import { AAA, BBB, CCC } from 'path/to/index';

import * from 'path/to/index'

といった形ですね。

クラス内にクラスを定義する

C#などの言語では class 構文の内側に class を書いたりしますが、 TypeScript ではそうはいきません。
TypeScript では namespace 構文を利用して似たようなことができます

class Outer {
    /*
    実装
    */
}
namespace Outer {
    export class Inner {
        /*
        実装
        */
    }
}
new Outer.Inner();

オーバーロードを控えてみる

TypeScript ではメソッドのオーバーロードもできるのですが、少々やり方が特殊になるので、
私は先に書いたクラス内に別なオブジェクトの定義を行う方法で Interface を定義して、それを引数とすることでオーバーロードを減らしています。

class Gaprot {
    constructor(params: Gaprot.Params) {
        console.log(`Hello ${params.name}`);
    }
}
namespace Gaprot {
    export interface Params {
        name: string;
        age?: number;
    }
}
new Gaprot({
    name: `ギャップラー`,
});

メソッドの this を束縛する

実は TypeScript のメソッドはインスタンスの this に紐付いていません。
なので、 JavaScript でしばしば初学者を困らせる「 this が違う!」が発生します。
コールバックとして自身のメソッドを渡した時によく発生しますね。
仕様上仕方ないので、 this を bind して置き直します。柔軟ですね。

class Gaprot {
    constructor(private name: string) {
        // これをしないと name undefined となります。
        this.method = this.method = this.method.bind(this);
        setTimeout(this.method, 1000);
    }
    private method() {
        console.log(`name ${this.name}`)
    }
}
new Gaprot(`gaprot`);

@types を使う

TypeScript では型定義のない JavaScript を読み込む際、型定義の d.ts ファイルを読み込ませることが多々あります。
その型定義を取得するために typings などのツールがありましたが、今は多くは npm の@types で入手することができます。 例えば Promise の実装の q の型定義であれば

npm install --save-dev @types/q

のように取得できます ものによっては npm でインストールした時点で型定義が付いてくるパッケージもありますね。
JavaScript 界隈はツールの量が多いので、なるべく依存するツールは減らしたいところです。

Webpack を使う

Web コンテンツの製作に使うならば TypeScript のコンパイラの tsc を直接使わずに
Webpack と ts-laoder を使うのがオススメです。 その際、 webpack.config.js を webpack.config.dev.js のように複数作り、 npm scripts で

scripts: {
    "build:dev": "webpack --config webpack.config.dev.js",
    "build:stg": "webpack --config webpack.config.stg.js"
}

のようにするといいと思います。これで

npm run build:dev
npm run build:stg

のようにビルドできます。 gulp を使ったり config ファイル内で分岐するなどして、コンフィグファイル1つで複雑なこともできはしますが、
他人に伝えるのが大変になるので私は最近は控えるようにしています。

静的解析を行う

JavaScript に ESLint があるように TypeScript には TSLint があります。
TypeScript の構文は JavaScript とほとんど変わらないので、結構自由です。
それをルールに従ったコードになるように促してくれるのが主な役割ですね。
記事中のコードにも ; を抜いている部分がありますが、気づきましたでしょうか?
気づかなかったひとは要チェック TSLint です。

async する

いつの間にやら tsc が async/await 構文を es5向けにビルドできるようになっていました。 tsconfig.json の compilerOptions の lib に es2015を追加すると es5向けでこの構文が使えるようになるみたいです。
なぜか node の型定義も必要でした。
非同期とループの組み合わせがとても書きやすくなりますね。

/// <reference_path='@types/node/index.d.ts' />;
class Gaprot {
    constructor(private name: string) {
    }
    async callAsync() {
        const items = this.name.split('');
        for(let i = 0; i < items.length; i++) {
            await this.delay(1000);
            console.log(items[i]);
        }
    }
    private async delay(time: number) {
        return new Promise<void>((resolve) => {
            setTimeout(resolve, time);
        });
    }
}
const gaprot = new Gaprot(`お疲れ様でした`);
gaprot.callAsync();

まとめ

TypeScript を始めるまでは面倒くさそうだなぁと思っていたのですが、やってみるとコーディングのストレスが減ってとってもいい感じでした。
なれると導入も簡単なので、これからもっと使っていけるといいですね。
C#や Java に似た構文でかけるのも非常に好印象です。しかし、動作するときは JavaScript なのである程度の経験者向きと言えます。
最近の流行りのライブラリやフレームワークも TypeScript でのやり方をドキュメントに記していてくれたり、
型定義ファイルやデコレータを提供してくれたりしているのも嬉しいところです。
さらなる進化に期待しつつ、腕を磨きましょう。



ギャップロを運営しているアップフロンティア株式会社では、一緒に働いてくれる仲間を随時、募集しています。 興味がある!一緒に働いてみたい!という方は下記よりご応募お待ちしております。
採用情報をみる