「静的型付けが出来るJavaScriptの上位言語」を触ってみよう。パート3 - TypeScript #3
前々回、前回と続けておりますこのシリーズ。 自身の直感を頼りに「触ってみよう」レベルの情報を連載しております。 (コアな情報は記載していません。あしからず。)
今日はTypeScript最終章でいきたいと思います。
本日の見出し
- Any
- thisの解決
- module(namespace)
- export
- import
- ファイルインポート
- *.d.ts
- 外部ライブラリの参照
- 宣言ソースファイルのコンパイル
- IDE
ではいってみましょ
Any
TypeScriptにはany
(aは小文字)と言う型があります。
これはなんだと言うと、なんでも型です。
これを指定すると全ての型チェックが無効になります。
え、そもそも型指定する為にTypeScript使うんじゃ...
こちらではこのように記載されています。
any は既存の JavaScript コードを移植するようなときや、JSON 形式のデータを扱うような場合に便利なことがあります。 また、any にキャストするとどんな式でもあらゆる型チェックを回避できるので、どうしてもうまく書けない時の回避策としても重要です。
そう。既存コードなど型を意識しないコードになんでも型を使用することで、柔軟に、簡潔に流用することが出来るような仕組みになっています。
thisの解決
JavaScriptでなにが厄介かと言うとこれです。this
の解決。
使用箇所よってthis
の中身は変化する為、いちいち気を使わないといけない。
TypeScriptではどのように解決できるかというと
class Foo { greet: string; say() { console.log(this.greet); // 1. } } var foo: Foo = new Foo(); foo.greet = 'hello'; foo.say();
こんなクラスがあったとして
- 「1.」で
this
を使用している - 「1.」を
this.foo
とか存在しないプロパティを指定するとコンパイルエラー(thisがFooクラスとして認識されていることがわかる)
実行すると
$ node usethis.js
hello
もちろん「hello」と自身のプロパティが表示される
ここまではわかる。型を指定出来ないクロージャとかでのthis
の扱いが問題。
例えばsetTimeoutとかで指定するコールバック関数内でthis
を使用した場合
class Foo { greet: string; say() { setTimeout(function() { console.log(this.foo); // 1. }, 1000); } } var foo: Foo = new Foo(); foo.greet = 'hello'; foo.say();
実行すると
$ node usethis.js
undefined
undefined。this
が解決されていない...
こういう場合、TypeScriptは↓のように定義することでthis
が解決出来る
class Foo { greet: string; say() { setTimeout(() => { console.log(this.greet); }, 1000); } } var foo: Foo = new Foo(); foo.greet = 'hello'; foo.say();
- 実行すると「hello」が表示される
この() => { ... }
のような関数定義をアロー関数と呼ぶらしい。詳しくはこちら
アロー関数のコンパイル済JavaScriptはこんな感じ
... var _this = this; setTimeout(function () { console.log(_this.greet); }, 1000); ...
_this
変数へ退避されている
module(namespace)
TypeScriptはmodule
キーワードを使って名前空間(パッケージ)を表現することができる模様
export
module
キーワードを使って定義した名前空間にクラスを追加するにはexport
キーワードを使用する
module utils { export class Foo { greet: string; say() { setTimeout(() => { console.log(this.greet); }, 1000); } } } new utils.Foo(); // new Foo(); // ←これはコンパイルエラー
import
名前空間が長い場合、import
キーワードを使用して省略できる
module com.hatenablog.koizuss.utils { export class Foo { ... } } import utils = com.hatenablog.koizuss.utils; new utils.Foo();
- Javaのような
import com.hatenablog.koizuss.utils.Foo
やimport com.hatenablog.koizuss.utils.*
のようなインポートはNG
ファイルインポート
export
した別ファイルをインポートすることもできる
import utils = module("utils");
これは↓と等価
var utils = require("./utils");
- 別ファイルとしての
export
やrequire
については前回参照
*.d.ts
宣言ソースファイルというらしい。既存JavaScriptを元にTypeScriptとしての定義のみを書き出したファイル。
宣言ソースファイルを使うことで、例えば自分で実装していない既存JavaScriptライブラリの定義のみを参照し、TypeScriptとして実装できる
- 参照するだけで使用したライブラリが勝手にインポートされるわけではないので注意
外部ライブラリの参照
有名どころライブラリの宣言ソースファイルはこちら
宣言ソースファイルは↓のようなコメントを記載することで参照できる
/// <reference path="hoge.d.ts" />
宣言ソースファイルのコンパイル
勘違いをしておりまして。過去の記事で「jsファイルからのコンパイル」と書いていましたが、JavaScriptファイルから宣言ソースファイルを生成する方法はありませんでした。すいません。
TypeScriptでJavaScriptをライブラリとして展開する場合に、宣言ソースファイルを実装した.ts
ファイルから生成することができます。
↓のようにコンパイラで--declaration
(or -d
)オプションを加えてコンパイルすることで宣言ソースファイルを出力できます。
$ tsc --declaration sample.ts
sample.ts
で実装した型定義がsample.d.ts
ファイルとして出力されます
IDE
ここまで自身は全てテキストエディタで実装し、コマンドでコンパイルしてきました。
実際にTypeScriptを使ってアプリケーションを作成するのであれば、IDEを使うことが理想的です。
TypeScriptの型解決はIDE上で更に効果を発揮します。
なぜなら、型が定まっていることで、充実したコード補完やリアルタイムにコンパイルエラーを確認できるからです。
デモ動画を見てもらうとその効果がよく分かるのではないかと思います。
で、じゃTypeScriptはどんなIDEに対応しているか。
↑の動画で使用されているVisual Studio 2012
を初め、以下のエディタを公式にサポートをしています。
後、IntelliJはプラグインがある模様(JetBrainsすげぇ!!ということはWebStormでも使える?!)
さてさて。
ここまで3回にわたってTypeScriptをざっくり触ってきたんですが、感想をこれまたざっくりとまとめたいと思います。
- やっぱり型解決できているって健康的
- アプリケーションは複数のデータと処理を統合することで動きます。他のデータ・処理がどのような責務を持ってどのように振る舞うかを型によって表現し保証することでスムーズな連携を可能とします。 JavaScript上で他の部品を使用する場合の言い知れぬ不安を、静的に型を定義することで事前に解決することができます。 これは精神衛生上かなり健康的!!
- オブジェクト指向プログラミング(OOP)に適している
- 素のJavaScriptでもOOPは可能です(若干異論ありそうな気がしますが...)。でもこれじゃない感が拭えません。 やはりOOPは型と言う概念があっての賜物かなと自身は思います。(OOPのメリットは割愛)
- IDEって素敵やん
- 型を定義する = 記述するコード量が増えます。 以前に記載した通り、コンパイル済JavaScriptに型は一切反映されません。と言うことは実際動くプログラム以外のコードを書く必要があります。 しかし!!ここでIDEがいい仕事をします。IDEが型を理解することで↑に挙げたような入力補完やコンパイルエラーのリアルタイム検知ができます。 コードを書く量は増えますが、このようなIDE機能を活用することで、実質的にコードをタイプする量は、型解決しないコードを書く場合と大差ないと思われます。 このようなIDEの機能を使用できるのも型と言う概念があってこそです。
柔軟性に優れている
- TypeScriptとして自身が一番優れていると感じた点はここです。素のJavaScriptも書ける。 例えば、別の処理が使用する振る舞いは、型を意識してきっちり、 振る舞いの中身等、内部に隠蔽されたスコープでは型を意識せず、さくっと、 みたいな部分的な使い分けが可能です。 造るモノ・ヒトに応じたいい塩梅を実現できます。
既存コードがそのまま動作する
- ↑でも挙げましたがTypeScriptはJavaScriptをそのまま記述できます。と言うことは既存資源をそのまま流用できます。 今JavaScriptでなにかしようとした場合、ライブラリを使用しないことはほぼ皆無です。既存のライブラリをそのまま使用できるメリットは非常に大きいと思います。 (使えるというだけで、素のJavaScriptは型解決できません。型を含めた使用は宣言ソースファイルが必要です)
次回
今度は同じノリでHaxeを試してみたいと思います。