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