On Github seanchas116 / slide-learn-typescript
Nov. 20, 2015@seanchas_t
Canvas要素を使ったお絵かきをTypeScriptで
https://github.com/seanchas116/learn-typescript
分からなくなったら見てください
個人的おすすめ
↓ TypeScriptコンパイラ (tsc)
↓ Browserify (watchify)
mkdir learn-typescript && cd learn-typescript npm init
# 安定版 npm install --save-dev typescript # nightly版 (機能が多い / 冒険したい人におすすめ) npm install --save-dev typescript@next
npm install --save-dev watchify
TypeScriptのプロジェクト管理に使われるファイル
{ "compilerOptions": { "target": "es5", "module": "commonjs", "moduleResolution": "node", "noImplicitAny": true, "inlineSourceMap": true, "inlineSources": true }, "files": [ ] }
詳しい説明はTypeScriptのWikiへ
atom-typescriptを使う人はさらに
... "filesGlob": [ "**/*.ts", "!node_modules/**" ], "compileOnSave": false, "atom": { "rewriteTsconfig": true } }
package.json の scripts フィールドを使うnpm run ... で実行
{ ... "scripts": { "tsc": "tsc -w", "watchify": "watchify -d src/index.js -o bundle.js" }, ... }
package.json
<!DOCTYPE html> <html> <head> <title>TypeScript Canvas</title> </head> <body> </body> <script src="bundle.js"></script> </html>
const canvas = document.createElement("canvas"); canvas.width = 800; canvas.height = 600; document.body.appendChild(canvas); const ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.fillStyle = "#abc"; ctx.rect(100, 100, 200, 200); ctx.fill();
src/index.ts
"files": [ "src/index.ts" ]
atom-typescript だと自動で追加してくれます
const canvas = document.createElement("canvas"); canvas.width = 800; canvas.height = 600; document.body.appendChild(canvas); const ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.fillStyle = "#abc"; ctx.rect(100, 100, 200, 200); ctx.fill();
const canvas = document.createElement("canvas");
canvas: HTMLCanvasElement
const ctx = canvas.getContext("2d");
ctx.wrongMethod(1, 2, 3);
error TS2339: Property 'wrongMethod' does not exist on type 'CanvasRenderingContext2D'.
interface Path { x: number; y: number; draw(ctx: CanvasRenderingContext2D): void; } export = Path;
src/Path.ts
"files": [ "src/index.ts", "src/Path.ts", ]
import Path = require("./Path"); function fillPath(color: string, path: Path) { ctx.beginPath(); ctx.fillStyle = color; path.draw(ctx); ctx.fill(); } const rect = { x: 100, y: 200, w: 100, h: 100, draw(ctx: CanvasRenderingContext2D) { ctx.rect(this.x, this.y, this.w, this.h); } }; fillPath("#abc", rect);
src/index.ts
TypeScript の型の基本は interface
interface: 必要なプロパティ(メソッド)の集合
interfaceの条件を満たすと代入が成功する(ダックタイピング / 構造的部分型)
function foo(a: number, b: string) { return 100; } type FooType = typeof foo; type FooType = (a: number, b: string) => number; interface FooType { (a: number, b: string): number; }
すべて同じ意味
Import Require のほうが今のところ安心
"compilerOptions": { "target": "es5", "module": "commonjs", ... }
class Circle { constructor(public x: number, public y: number, public r: number) {} draw(ctx: CanvasRenderingContext2D) { ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, true); } static unit() { return new Circle(0, 0, 1); } } export = Circle;
src/Circle.ts
class Rectangle { constructor(public x: number, public y: number, public w: number, public h: number) {} draw(ctx: CanvasRenderingContext2D) { ctx.rect(this.x, this.y, this.w, this.h); } } export = Rectangle;
src/Rectangle.ts
const rect = new Rectangle(100, 100, 200, 300); const circle = new Circle(300, 300, 50); fillPath("#abc", rect); fillPath("#cba", circle);
src/index.ts
"files": [ ... "src/Circle.ts", "src/Rectangle.ts" ]
class → interface型 かつ new呼び出しできる値
interface Circle { x: number; y: number; r: number; draw(ctx: CanvasRenderingContext2D) void; } interface CircleStatic { new (x: number, y: number, r: number): Circle; unit(): Circle; } var Circle: CircleStatic = ...
lodashを使って
function randomColor() { return "#" + _.sample("0123456789ABCDEF".split(""), 6).join(""); }
// require() 関数の型定義 declare function require(module: string): any; const _ = require("lodash"); // _: any (制約のない型) function randomColor(): string { // anyなのでどんなメソッドも呼べる return "#" + _.sample("0123456789ABCDEF".split(""), 6).join(""); }
JavaScript (CommonJS) の require を直接使う
ライブラリは any 型で扱う
const _: LodashStatic = require("lodash"); declare function require(module: string): any; interface LodashStatic { sample<T>(array: T[], count: number): T[]; } function randomColor() { // 型チェックされてOK return "#" + _.sample("0123456789ABCDEF".split(""), 6).join(""); }
interface を使って自分で型情報を書く
import _ = require("lodash"); function randomColor() { return "#" + _.sample("0123456789ABCDEF".split(""), 6).join(""); }
import require 構文で lodash を require
型定義が別に必要
型定義が集まっているリポジトリ DefinitelyTyped
型情報管理ツール tsd を使う
npm install tsd -g tsd init # tsd.json 作成 tsd install lodash --save
typings ├── lodash │ └── lodash.d.ts └── tsd.d.ts tsd.json
DefinitelyTyped から型情報を取ってきて管理する
"files": [ ... "typings/tsd.d.ts" ]
declare var _: _.LoDashStatic; declare module _ { interface LoDashStatic { sample<T>(collection: Array<T>, n: number): T[]; ... } } declare module "lodash" { export = _; }
import _ = require("lodash"); function randomColor() { return "#" + _.sample("0123456789ABCDEF", 6).join(""); }
declare module _ { interface List<T> { [index: number]: T; length: number; } interface LoDashStatic { sample<T>(collection: List<T>, n: number): T[]; ... } }
stringはList<T>を満たすので、_.sampleにstringも渡せる
function randomColor() { return "#" + _.sample("0123456789ABCDEF", 6).join(""); } const circles = _.times(200, () => ({ fill: randomColor(), path: new Circle(Math.random() * 800, Math.random() * 600, 10) })); for (const {fill, path} of circles) { fillPath(fill, path); }
src/index.ts