DACエンジニアブログ:アドテクゑびす界

DACのエンジニアやマーケター、アナリストが執筆するアドテクの技術系ブログです。

TypeScriptについてまとめてみた

はじめに

JavaScript がとりあえずそのまま動くので、雰囲気で使ってしまいがちな TypeScript。初心者向けに基本的なことをまとめてみました。

TypeScript って何?

TypeScript はマイクロソフトが開発したプログラミング言語で、オープンソースでメンテナンスされています。分類としては altJS の一つ。JavaScript の不足している機能を改良した言語で、JavaScript に変換(コンパイル)して実行します。

CoffeeScript と何が違うの?

altJS といわれる言語には TypeScript のほかにも Ruby on Rails で使われている CoffeeScript などがあります。CoffeeScript とは何が違うのか、同じ処理を JavaScript(ECMAScript 5)、CoffeeScript、TypeScript で書いて比較してみます。

JavaScript(ECMAScript 5)
function Person(name) {
  this.name = name;
}
Person.prototype.say = function(message, count) {
  for (var i = 1; i <= count; i++) {
    console.log(this.name + " says " + message + " " + i);
  }
}

var person = new Person("Bill");
person.say("Hello!", 3);

ECMAScript 5 までの JavaScript は class構文がないため、クラス定義に prototype を使う必要があり、オブジェクト指向プログラミングを行うには工夫が必要でした。

CoffeeScript
class Person
  constructor: (@name) ->
  say: (message, count) ->
    for i in [1..count]
      console.log """#{@name} says #{message} #{i}""";

person = new Person "Bill";
person.say "Hello!", 3;

CoffeeScript だとクラス定義をすっきり書けますが、関数呼び出しやループが Ruby に近い書き方になっていて JavaScript とはだいぶ違っています。

TypeScript
class Person {
  constructor(private name: string) {}
  public say(message: string, count: number) {
    for (let i = 1; i <= count; i++) {
      console.log(`${this.name} says ${message} ${i}`);
    }
  }
}

const person = new Person("Bill");
person.say("Hello!", 3);

TypeScript では JavaScript のコードをそのまま使えます。ループや条件分岐、関数呼び出しなどの JavaScript の構文はそのままで、それに追加して静的型付けやクラス宣言ができるようになっています。

JavaScript と何が違うの?

TypeScript を使う一番のメリットは、間違った型の代入を防げること(静的型付け)です。

JavaScript
var count;
count = 0;
count ++;
count = "Steve";

JavaScript では変数の型を指定することができないため、数値として使っている変数(count)に文字列("Steve")を代入することができてしまいます。

TypeScript
var count: number;
count = 0;
count ++;
count = "Steve"; // コンパイルエラー

TypeScript では、変数の型(number)を指定することができるので、数値として使っている変数(count)に文字列("Steve")を代入すると、コンパイルエラーとして指摘されます。

実は、このコロン(:)を使った静的型付けは JavaScript の仕様である ECMAScript 4 で提案されていたのですが、ECMAScript 4 はお蔵入りとなってしまいました……なので、JavaScript で型チェックを行いたい場合は TypeScript などの altJS が必要になるのです。

また、TypeScript 独自の型情報は JavaScript にコンパイルすると消えてしまいます(型消去)。

TypeScript(コンパイル前)
interface IMessage {
  text: string;
  count: number;
}

var message: IMessage = { text: "Hello!", count: 3 };
var i: number;
for (i = 1; i <= message.count; i++) {
  console.log(message.text + " " + i);
}
JavaScript(コンパイル後)
var message = { text: "Hello!", count: 3 };
var i;
for (i = 1; i <= message.count; i++) {
  console.log(message.text + " " + i);
}

コンパイル後のコードを見ると、インタフェース(IMessage)は定義から綺麗さっぱり無くなっていて、コロン(:)の後ろにある型情報も無くなっています。

ECMAScript 6 と何が違うの?

class構文によるクラスの定義など TypeScript のいくつかの機能は ECMAScript 6(ECMAScript 2015)を先取りしたものです。TypeScript から JavaScript へのコンパイル時にはターゲットとして ECMAScript 3、5、6(2015)、2016、2017 が指定できて、それぞれ変換結果が異なります。

TypeScript
interface IMessage {
  text: string;
  count: number;
}

class Person {
  constructor(private name: string, private age: number) {}
  public say = (message: IMessage) => {
    for (let i = 1; i <= message.count; i++) {
      console.log(`${this.name}(${this.age}) says ${message.text} ${i}`);
    }
  }
}
const person = new Person("Bill", 62);
person.say({ text: "Hello!", count: 3 });
JavaScript(ECMAScript 5 をターゲットにしてコンパイルした結果)
var Person = /** @class */ (function () {
  function Person(name, age) {
    var _this = this;
    this.name = name;
    this.age = age;
    this.say = function (message) {
      for (var i_1 = 1; i_1 <= message.count; i_1++) {
        console.log(_this.name + "(" + _this.age + ") says " + message.text + " " + i_1);
      }
    };
  }
  return Person;
}());
var person = new Person("Bill", 62);
person.say({ text: "Hello!", count: 3 });
JavaScript(ECMAScript 6 をターゲットにしてコンパイルした結果)
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this.say = (message) => {
      for (let i = 1; i <= message.count; i++) {
        console.log(`${this.name}(${this.age}) says ${message.text} ${i}`);
      }
    };
  }
}
const person = new Person("Bill", 62);
person.say({ text: "Hello!", count: 3 });

ターゲットを変えてコンパイルすると、型消去が行われている点は同じなのですが、class構文や letconst、アロー構文、テンプレートリテラルなど ECMAScript 6 標準の機能の変換の有無で違いが出ています。また ECMAScript 5 では thislet で定義された変数のスコープを配慮して変数名が変更されていることもわかります。

ECMAScript 6 の機能に加えて静的型付けなどが使えて、ECMAScript 5 しか対応していないブラウザ向けにも変換できるのが TypeScript ということですね。

動かしてみる

簡単に実行するには TypeScript Playground を使ってブラウザで確認できます。 ローカルで実行する場合は npm で tsc コマンドをインストールして TypeScript のファイルを JavaScript に変換(コンパイル)できます。

$ npm install -g typescript
$ tsc hello.ts

統合開発環境としては Visual Studio Code が便利です。

参考

※ DACはエンジニアを募集しています! 興味のある人はバナー(↓)からどうぞ