9
13

More than 5 years have passed since last update.

TypeScriptのイケてるところと残念なところ

Last updated at Posted at 2016-09-13

TypeScriptとは

MicrosoftのC#リードプログラマが作った
JavaScriptにコンパイルできるラッパー言語
文法はC#とJavaScriptを足して2で割ったような文法
ES6にもいくつかの文法は利用されている。
最大の特徴は変数に型定義をする必要がある静的言語である。
JavaScriptは実行時型決定の動的言語であり、
同じ変数に別の型のデータを代入して上書きできたりする。
動的言語は自由度が高い代わりに実行時エラーなど実行しなければ出現しないエラーが存在する。(コード品質はプログラマの力量依存しやすい)
それに対し、静的言語はコンパイラによる型チェックがあるため、変数の意図されない使われ方を事前に防ぐことができる。
(コード品質が一定以上担保されるため、大規模開発に向いてる)

TypeScriptのインストールは下記コマンドできる。

npm install -g typescript
npm install -g typings

インストールできれば、tscコマンドが使える。

tsc --help

サンプル

nodejsのサーバを立てるサンプル(app.ts)

app.ts
///<reference path='typings/index.d.ts' />
import http = require("http");
class Main
{
    constructor()
    {
        // httpサーバーを設定する
        var server:http.Server = http.createServer((request, response) => this.requestHandler(request, response));
        // サーバーを起動してリクエストを待ち受け状態にする
        server.listen("3000");
    }

    /*
    * サーバーにリクエストがあった時に実行される関数
     */
    private requestHandler(request, response):void
    {
        response.end("Hello! Node.js with TypeScript");
    }
}
var main:Main = new Main();

TypeScriptからJavaScriptのモジュールを利用する際は型定義ファイルが必要になる。
このサンプルではNodeJSを利用しているため、
NodeJSの型定義ファイルをインストールする必要がある。
typingsモジュールは型定義ファイルをバージョン管理しているツールで
typingsコマンドより型定義ファイルをダウンロードできる。

typings install dt~node --save --global

--saveオプションをつけるとtyping.jsonが作成され、依存モジュールを設定ファイルに保存できる。
gitなどには作成されるtypingsフォルダではなく、typing.jsonを保存すればよい。
下記コマンドでtyping.jsonに記載された依存型定義ファイルを一括ダウンロードできる。

typings install

app.ts冒頭の3重コメントアウトされたreferenceタグに型定義ファイルのパスを指定する。
TypeScriptコンパイル時にこのパスに従って型定義ファイルが参照される
(型定義ファイルは*.d.tsという拡張子)

app.ts
///<reference path='typings/index.d.ts' />

次のコマンドでコンパイル

tsc app.ts --module commonjs

次のようなapp.jsが出力される。(ES5)

"use strict";
///<reference path='typings/index.d.ts' />
var http = require("http");
var Main = (function () {
    function Main() {
        var _this = this;
        // httpサーバーを設定する
        var server = http.createServer(function (request, response) { return _this.requestHandler(request, response); });
        // サーバーを起動してリクエストを待ち受け状態にする
        server.listen("5000");
    }
    /*
    * サーバーにリクエストがあった時に実行される関数
     */
    Main.prototype.requestHandler = function (request, response) {
        response.end("Hello! Node.js with TypeScript");
    };
    return Main;
}());
var main = new Main();

いつものNodeJSコマンドで実行

node app.js

ES6(ES2015)にコンパイルするにはコンパイルオプションをつけると変換できる

tsc app.ts --module commonjs --target es6

次のようにES6のJavaScriptに出力される。

"use strict";
///<reference path='typings/index.d.ts' />
const http = require("http");
class Main {
    constructor() {
        // httpサーバーを設定する
        var server = http.createServer((request, response) => this.requestHandler(request, response));
        // サーバーを起動してリクエストを待ち受け状態にする
        server.listen("5000");
    }
    /*
    * サーバーにリクエストがあった時に実行される関数
     */
    requestHandler(request, response) {
        response.end("Hello! Node.js with TypeScript");
    }
}
var main = new Main();

tsconfig.json

tscコマンドのコンパイルオプションを設定ファイルに書くことができる(tsconfig.json)

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6"
  },
  "exclude": [
    "node_modules"
  ]
}

tsconfig.jsonがあるフォルダでtscコマンド実行で一括コンパイルできる

tsc

TypeScriptのモジュール化

export、importキーワードを使うことで
モジュールのエクスポートとインポートができる。
MyApp.tsにモジュール分離しexportしてみる。

MyApp.ts
///<reference path='typings/index.d.ts' />
import http = require("http");
class Main
{
    constructor()
    {
        // httpサーバーを設定する
        var server:http.Server = http.createServer((request, response) => this.requestHandler(request, response));
        // サーバーを起動してリクエストを待ち受け状態にする
        server.listen("5000");
    }

    /*
    * サーバーにリクエストがあった時に実行される関数
    */
    private requestHandler(request, response):void
    {
        response.end("Hello! Node.js with TypeScript");
    }
}

export default Main

利用側はimportでロードできる。

app.ts
import Main from './MyApp'

var main:Main = new Main();

デバッグ

Chromeでブラウザでのデバッグをするには
sourcemapファイルが必要です。
次のtest.tsをデバッグしてみます。

test.ts
console.log("test");

sourcemapつきでコンパイルします。

tsc --sourcemap test.ts

map情報がtest.jsに出力されます。

test.js
//# sourceMappingURL=test.js.map

index.htmlにてtest.jsを読み込みます。

index.html
<script type="text/javascript" src="test.js"></script>

Developer Toolの設定よりEnable JavaScript source mapsを有効にします。

enablemap.png

ブラウザをリロードするとtsファイルが読み込まれ、ブレークポイントを設定できるようになります。

debug.png

既存JavaScriptライブラリの型定義ファイルの自作が鬼門

NodeJSやjQueryなど最大手ライブラリに関しては
型定義ファイルは用意されていてtypingsモジュールでダウンロードできるが(DefinitelyTyped
現状、型定義ファイルが用意されていない既存ライブラリ(NodeJS,フロントエンド)がほとんどというか普通に最初からTypeScript意識して作ってなきゃ無理


既存のJavaScriptライブラリでTypeScript対応していないライブラリは型定義ファイルを自作しないといけない
→型定義ファイル作成のコストが非常に高い(既存ライブラリの構造を読み解いた上で型推測しないといけない・・・特に返却型が異なる関数とかanyにせざる得ない)
→ライブラリ自体のバージョンが上がった時の保守は?
→定義ファイル自体が間違っている場合のチェックは?

型定義ファイルの作成に関して良い感じにまとまってるところがなかった
簡単なサンプルなら次を参考:TypeScriptで自作jsファイルを読み込んでみる(.d.ts)

JavaScriptから型定義ファイルを自動生成するツールはあるが確実ではない(α版)
TypeScript型定義ファイルのコツと生成ツール dtsmake

所感

型チェックでコンパイル時に実行時エラーを防げるのは利点だが
結局のところJavaScriptの知識は必要になると思う。(型定義ファイルの自作やコンパイルしたソースコードが意図したものか検証が必要)
その上でTypeScriptの文法が必要になるため、トータルの学習コストが高い。
既存サードパーティのJavaScriptライブラリの型定義ファイルを自作となると問題が色々でる。(特にバージョンアップに伴う保守)

思想自体は悪くはないのですが、既存ライブラリの使い回し部分でまだまだいけてないと思いましたまる
ただし、自作でライブラリを公開しますってときはTypeScriptで書くのは悪くないと思いました。

あと、gulpで自動化しないと色々めんどくさいと思いました。
TypeScript(ES6)→babel(ES5)→uglify→browserify→gulp-webserver

9
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
13