LoginSignup
37
44

More than 5 years have passed since last update.

TypeScript速習会

Last updated at Posted at 2015-11-24

先日 (2015年11月20日) に行われたTypeScript速習会@Wantedly の発表内容です。実際にコードを書いてみるという形でTypeScriptの使い方を紹介しました。

今日紹介すること

  • TypeScriptプロジェクトのセットアップ
  • 基本的なTypeScriptのコードを書く
  • 既存のJavaScriptライブラリを使う

TypeScript の紹介

TypeScript とは?

  • JavaScript + 静的型付け
    • (ほぼ) JavaScript の上位互換
  • 最新JavaScript (ES2015以降) の文法も積極的に取り入れている

TypeScript のうれしい所

  • 型チェックでエラーを未然に発見
  • エディタ上でのコード補完 / リファクタリング
  • JavaScript / CoffeeScript などとの共存

TypeScript のうれしくない所

  • JavaScriptの悪い点も引き継いでいる
  • anynull等による型安全性の穴

TypeScript リンク

早速書いてみよう!

今日書く内容

Canvas要素を使ったお絵かきをTypeScriptで

セットアップ

今回のプロジェクト構成

TypeScript ファイル (.ts)

↓ TypeScriptコンパイラ (tsc)

JavaScript ファイル (.js)

↓ Browserify (watchify)

bundle.js

package.json

mkdir learn-typescript && cd learn-typescript
npm init

ツールのインストール

TypeScript

# 安定版
npm install --save-dev typescript
# nightly版 (機能が多い / 冒険したい人におすすめ)
npm install --save-dev typescript@next

watchify

npm install --save-dev watchify

tsconfig.json

TypeScriptのプロジェクト管理に使われるファイル

tsconfig.json
{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "moduleResolution": "node",
        "noImplicitAny": true,
        "inlineSourceMap": true,
        "inlineSources": true
    },
    "files": [
    ]
}

詳しい説明はTypeScriptのWiki

tsconfig.json + atom-typescript

atom-typescriptを使う人はさらに

tsconfig.json
  ...
  "filesGlob": [
      "**/*.ts",
      "!node_modules/**"
  ],
  "compileOnSave": false,
  "atom": {
      "rewriteTsconfig": true
  }
}

ビルドタスク

package.json の scripts フィールドを使う

npm run ... で実行

package.json
{
  ...
  "scripts": {
    "tsc": "tsc -w",
    "watchify": "watchify -d src/index.js -o bundle.js"
  },
  ...
}

  • npm run tsc (tsc -w)
    • TypeScriptを監視してコンパイル
  • npm run watchify (watchify -d src/index.js -o bundle.js)
    • Browserify (watchify で監視してバンドル)

index.html

index.html
<!DOCTYPE html>
<html>
  <head>
    <title>TypeScript Canvas</title>
  </head>
  <body>
  </body>
  <script src="bundle.js"></script>
</html>

用意したファイル

  • package.json
    • 全般的プロジェクト設定
    • 依存パッケージ設定
    • ビルドタスク設定
  • tsconfig.json
    • TypeScriptのプロジェクト設定
  • index.html
    • ブラウザでロードするHTML

コードを書こう

簡単な描画

src/index.ts
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();
tsconfig.json
  "files": [
    "src/index.ts"
  ]

atom-typescript だと自動で追加してくれます

コンパイル

  • npm run tsc
  • npm run watchify
  • index.htmlを開く

描画結果

hello.png

JavaScript (ES6) とおなじ?

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: CanvasRenderingContext2D

変なメソッド呼び出しをすると…

ctx.wrongMethod(1, 2, 3);
error TS2339: Property 'wrongMethod' does not exist
on type 'CanvasRenderingContext2D'.

interface

src/Path.ts
interface Path {
  x: number;
  y: number;
  draw(ctx: CanvasRenderingContext2D): void;
}
export = Path;
tsconfig.json
  "files": [
    "src/index.ts",
    "src/Path.ts",
  ]
src/index.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);

描画結果

rect.png

TypeScript の型とは?

TypeScript の型の基本は interface

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;
}

すべて同じ意味

TypeScript のモジュール管理

  • ES6 スタイル (新しい)
    • ES6 のモジュール構文を使う
  • Import Require (昔からある)
    • CommonJS風モジュール構文
    • import Foo = require("./Foo")
    • export = Foo
    • 今回はこちらを使用

TypeScript のモジュール: どちらを使う?

Import Require のほうが今のところ安心
* 古くからある型定義ファイルは

こちらを前提にしている
* ES6 スタイル と Import Require の互換性が低い
* 今後改善される可能性あり

TypeScript 1.5.3 変更点 - Qiita で紹介されているこちらの方法 を使えば、ES6 スタイルを使っても特に問題ないことがわかった (今まで勘違いをしていた…)

tsconfig.json 設定

tsconfig.json
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    ...
  }
  • "module": "commonjs"
    • Import Require を使う -> CommonJS へコンパイルするよう設定
  • "target": "es6""module" を指定しない
    • ES6 スタイルになる

class

src/Circle.ts
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/Rectangle.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/index.ts
const rect = new Rectangle(100, 100, 200, 300);
const circle = new Circle(300, 300, 50);
fillPath("#abc", rect);
fillPath("#cba", circle);
tsconfig.json
  "files": [
    ...
    "src/Circle.ts",
    "src/Rectangle.ts"
  ]

描画結果

rectcircle.png

class も interface で表せる

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 = ...

JavaScriptライブラリを使う

例: ランダムな色をつくりたい

lodashを使って

function randomColor() {
  return "#" + _.sample("0123456789ABCDEF".split(""), 6).join("");
}

やり方1: JavaScript の require

// 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 型で扱う

やり方2: 自分で型情報を書く

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 を使って自分で型情報を書く

やり方3(本命): DefinitelyTyped

import _ = require("lodash");

function randomColor() {
  return "#" + _.sample("0123456789ABCDEF".split(""), 6).join("");
}

import require 構文で lodash を require

型定義が別に必要

型定義が集まっているリポジトリ DefinitelyTyped

tsd

型情報管理ツール tsd を使う

npm install tsd -g
tsd init # tsd.json 作成
tsd install lodash --save
typings
├── lodash
│   └── lodash.d.ts
└── tsd.d.ts
tsd.json

DefinitelyTyped から型情報を取ってきて管理する

tsconfig.jsonに追加

tsconfig.json
  "files": [
    ...
    "typings/tsd.d.ts"
  ]

lodash.d.ts

declare var _: _.LoDashStatic;

declare module _ {
  interface LoDashStatic {
    sample<T>(collection: Array<T>, n: number): T[];
    ...
  }
}

declare module "lodash" {
  export = _;
}

実はstringも使える

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[];
    ...
  }
}

stringList<T>を満たすので、

_.samplestringも渡せる

実際に使ってみる

src/index.ts
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);
}

描画結果

random.png

37
44
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
44