楽して作るtypescriptの定義ファイル
こんにちわ、久々の投稿になります。セイカイです。
最近飼い猫が増えたので、猫について延々語ろうかと思いましたが、怒られそうなのでやめておきます。
さて、今回は弊社開発中のHerlockを例にTypeScriptの定義ファイルのお話です。
TypeScriptが何かとか、どこが良いのかという話は今回は触れませんのであしからず。
目次
- 外部ライブラリと定義ファイル
- 定義ファイル作成の自動化
- サンプルコード
- まとめ
外部ライブラリに関して
TypeScriptで外部ライブラリを使用するには定義ファイルを用意し、ライブラリのjsの内容をTypeScriptのコンパイラに橋渡ししてやる必要があります。
一般的なライブラリに関しては、有り難い事にDefinitelyTypedに纏まっていますので通常はこの中のものを使うと良いでしょう。
https://github.com/borisyankov/DefinitelyTyped
弊社でのケースですと、現在開発中のHerlockへの対応というものがありました。
この場合、上記に定義ファイルはありませんので自作する必要がありました。
ちなみにビルトインされるようなものの書き方はデフォルトのlib.d.tsが参考になります。
Functionですと下記の様になっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[sourcecode lang="javascript"]interface Function { apply(thisArg: any, ...argArray: any[]): any; call(thisArg: any, ...argArray: any[]): any; //bind(thisArg: any, ...argArray: any[]): Function; prototype: any; length: number; } declare var Function: { new (...args: string[]): Function; (...args: string[]): Function; prototype: Function; } [/sourcecode] |
直接classとしてではなくinterfaceとして定義する理由は、ビルトインオブジェクトはprototype継承出来ないという部分からですね。
Herlockのビルトインオブジェクトも同様にinterfaceとして定義してから同様の名前でグローバルにコンストラクタを定義する方式にしました。
尚、デフォルトのlib.d.tsはブラウザ実装のオブジェクトが多数定義されているので、Herlockでもnode等での利用時同様にこれらを省いたものを用意する方が良いですね。
※現在サービスとし公式にTypeScriptのサポートをしているわけではありませんのでご了承下さい。
定義ファイル作成の自動化
さてあとは作るだけですが、「正直めんどくさい」
docは既にあるので、ここから生成出来れば楽じゃないかというところで、自前でパーサー書くかと思いましたが、jsduckのexportオプションでjsonを吐き出せることを知ったのでコレを使います。
jsonファイルはこんな感じで生成します。
1 |
[sourcecode lang="text"]jsduck ./api --output ../json --export=full[/sourcecode] |
jsonからの定義ファイルの生成はphpを使うことにしました。
汎用性の無いヒドイ感じですが、とりあえず処理の叩きということで。
https://gist.github.com/herlock/7260543
処理内容的な部分は、内容見ながら文字列を生成しているだけですので、見れば分かる程度だと思いますが、注意点は型のマッピングを変更する必要があるものがあるということでしょうか。
例えばundefined=>void等
生成されたものは下記に上がっています
https://github.com/herlock/typescript
サンプルコード
最後は上記を使ったサンプルコードです。
画像を表示するスクリプトをTypeScriptで書いてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
[sourcecode lang="javascript"] class Sample { private stage:Stage; constructor() { this.stage = new Stage( 640, 640 ); addLayer( new Layer( this.stage ) ); } private onLoadHandler = (event:Event)=> { var image:Image = <Image>event.target; var bd:BitmapData = new BitmapData( image ); var bitmap:Bitmap = new Bitmap( bd ); this.stage.addChild( bitmap ); image.removeEventListener('load', this.onLoadHandler, false); }; public loadImage(src:string):void { var image:Image = new Image(src); image.addEventListener('load', this.onLoadHandler, false); } public static main(src:string):void { var sample:Sample = new Sample(); sample.loadImage(src); } } Sample.main("assets/images/image.png"); [/sourcecode] |
もう少し完結に書けますが、サンプルということでこういう形にしました。
これが、変換されて下記の様になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
[sourcecode lang="javascript"] var Sample = (function () { function Sample() { var _this = this; this.onLoadHandler = function (event) { var image = event.target; var bd = new BitmapData(image); var bitmap = new Bitmap(bd); _this.stage.addChild(bitmap); image.removeEventListener('load', _this.onLoadHandler, false); }; this.stage = new Stage(640, 640); addLayer(new Layer(this.stage)); } Sample.prototype.loadImage = function (src) { var image = new Image(src); image.addEventListener('load', this.onLoadHandler, false); }; Sample.main = function (src) { var sample = new Sample(); sample.loadImage(src); }; return Sample; })(); Sample.main("assets/images/image.png"); [/sourcecode] |
まとめ
typescriptに限らずaltJs系は外部ライブラリとの連携は、大体定義ファイルありきの形かと思いますので、既存のコードから上手く自動的に吐き出せる手段があると便利です。
jsduckのjsonを利用するのも一つの手で、データ化してしまえれば色々使えるかと思いますのでdocはきちんと書く方が良いですね。
typescript自体は1.xまでバージョンアップしてからが使い勝手の良くなるところだと思いますが、現状でも素で書くよりは楽なケースも多々あるかと思いますので、一考の余地はあるのではないでしょうか。
ということで、とりあえず猫の写真貼っておきます。