TypeScript + CreateJSで開発(1)

[話者] 以前、HTML5を使ってPC/スマートフォンで動作するゲームのようなものを作ったんだ。動作はJavaScriptで書くわけだ。


canvasタグを一枚表示して、そのcanvasタグに動く画像を表示するのはJavaScriptだと大変らしいので、CreateJSというライブラリを使用した。ActionScript3.0経験者にはCreateJSはなじみやすいということだったからね。


そのときはJavaScriptを直接書いたのだが、TypeScriptだとさらに便利になるらしい。クラスが書きやすいとか次世代JavaScriptの書き方を学べるとか。
TypeScriptを試すぞ。


[合いの手] TypeScriptってどうすれば使えるようになるの?


[話者] Windows環境では以下のようにした。


環境

  • TypeScript 1.5-alpha
  • CreateJS 2014.12.12 (EaselJS 0.8, TweenJS 0.6, SoundJS 0.6, PreloadJS 0.6)
  • Adobe Brackets 1.2
  • brackets-typescript 0.2.0
  • node.js 0.12.2
  • npm 1.4.9
  • Windows7 64bit

Windows7でポータブルなgitとnode.jsコマンドライン環境

https://gist.github.com/r-a-y/ead95fd11a1914657c29 の手順を参考にした。


TypeScript + CreateJS環境を準備

  • npm install typescript -g
  • npm install tsd -g
  • tsd init
  • tsd query easeljs --action install --resolve --save
  • tsd query soundjs --action install --resolve --save
  • EaselJS等は個別に取得必要? http://www.createjs.com/Downloads で easeljs-0.8.0.min.js 等を取得して作業フォルダーに配置。

TypeScript編集にAdobe Brackets を使う

  • 「.brackets.json」ファイルに以下を記して作業フォルダーに配置。 配置すると *.tsをsyntax color表示。
{
    "typescript": {
        "target": "ES5",
        "module": "CommonJS",
        "noImplicitAny": true,
        "sources" : [
            "**/*.ts",
        ]
    }
}  

  • フォント指定は[View - Theme]で可能
  • 補完決定をenterではなくtabにする [debug - show env file] "insertHintOnTab": true


TypeScriptとJavaScriptの比較


[合いの手] TypeScriptとJavaScriptの比較を見たいなあ。


[話者] 簡単なサンプルで違いを見てみよう。
JavaScriptだとこうなる。


sample01.html

<!DOCTYPE html>
<html>
<!-- なにか表示するだけ -->
<head>
<title>Try01</title>
<script src="easeljs-0.8.0.min.js"></script>
<script src="sample01.js"></script>
<style>
body {margin:0; padding:1em; background-color:rgba(127,127,127,0);
</style>
</head>

<body>
<canvas id="theCanvas" width="320" height="240"></canvas>
</body>

</html>


sample01.js

window.onload = function () {
  main();
}
function main() {
  var stage = new createjs.Stage('theCanvas');

  var obj = new createjs.Shape();
  obj.graphics.beginFill('#ff9999').drawCircle(0,0,50);
  obj.x = 100;
  obj.y = 100;
  stage.addChild(obj);

  createjs.Ticker.framerate = 30;
  createjs.Ticker.addEventListener('tick', handleTick);

  function handleTick(){
    obj.x += 5;
    if (obj.x > stage.canvas.width) obj.x = 0;
    stage.update();
  }
}


[合いの手] ふむふむ


[話者] これをTypeScriptで書くとこうなる。


sample22.html

<!DOCTYPE html>
<html>
<head>
<title>Try02</title>
<script src="easeljs-0.8.0.min.js"></script>
<script src="sample22.js"></script>
<style>
body {margin:0; padding:1em; background-color:rgba(245,245,245,1);
</style>
<script>
</script>
</head>

<body>
<canvas id="theCanvas" width="320" height="240"></canvas>
</body>

</html>


sample22.ts

/// <reference path="typings/easeljs/easeljs.d.ts" />
window.onload = function () {
    var m = new Main();
};
class Main {
    stage:createjs.Stage;
    obj:createjs.Shape;
    
    constructor() {
        this.stage = new createjs.Stage('theCanvas');

        this.obj = new createjs.Shape();
        this.obj.graphics.beginFill('#ff9999').drawCircle(0,0,50);
        this.obj.x = 100;
        this.obj.y = 100;
        this.stage.addChild(this.obj);

        createjs.Ticker.framerate = 30;
        createjs.Ticker.addEventListener('tick', this.handleTick);
    }

    handleTick = (event:createjs.Event):void => {
        this.obj.x += 5;
        if (this.obj.x > this.stage.canvas['width']) this.obj.x = 0;
        this.stage.update();
    }
};

このtsファイルを

$ tsc sample22.ts

コンパイルすると、sample22.js が作成されて実行可能になる。


[合いの手] HTMLは同じだね。
TSファイルの「this.」気になるな。


[話者] これはクラス内変数、クラス内関数にアクセスするとき必要になる。
クラス内変数、クラス内関数はclassの中で「var」とか「function」なしで宣言しているやつだ。それにはthisをつけるということだね。


ま、ちょっとソース読みにくくなるけど、逆にいうとクラス内変数を減らして、なるべく関数の引数とするよう自分を律する契機にはなるかも。


[合いの手]handleTick = (event:createjs.Event):void => {..}」はなんだろう?


[話者] これは関数定義だ。「function(){..}」と「() => {..}」はほぼ同じなんだ。しかし違いもある。コールバック関数で「function(){..}」を使うと、その関数内のthisは実行時のthisになるのでクラス変数にアクセスできなくて実行時に以下のエラーになる。

Uncaught TypeError: Cannot read property 'x' of undefined


[合いの手]handleTick = function(event:createjs.Event):void { this.obj.x ..}」と書くとthis.obj.xにアクセスできなくなっちゃうのか。

handleTick = (event:createjs.Event):void => { this.obj.x ..}」と書けばアクセスできるというわけだね。


ところで、コンパイルのとき --noImplicitAny というオプションを付けたほうがいい、と言われているよ。つけないの?


[話者] それをつけると、if (this.obj.x > this.stage.canvas['width']) this.obj.x = 0;の行でエラーになるんだ。

$ tsc --noImplicitAny sample22.ts
sample22.ts(23,26): error TS7017: Index signature of object type implicitly has an 'any' type.


このエラー消せなかった。まあ、TypeScriptを導入したのはクラスを楽に書きたいという理由が主なので、静的型付けはあんまりこだわらなくていいかな、と思ったので --noImplicitAny は使わないことにした。


[合いの手] this.stage.canvas.widthじゃなくて、this.stage.canvas['width']という書き方になってるのは理由があるの?


[話者] 前者だと

sample22.ts(23,26): error TS2339: Property 'width' does not exist on type 'Object | HTMLCanvasElement'.

というエラーになるんだ。理由は
http://www.atmarkit.co.jp/ait/articles/1501/29/news117.html
の記述にある「ドットを使ってプロパティにアクセスするとエラーになる」に該当するようだ。