Skip to content

TypeScriptとECMAScript:型システムと言語仕様の全貌

はじめに

TypeScriptを学び始める際、「ECMAScript」という言葉に出会うことがあります。JavaScriptとの関係は何となく知っていても、ECMAScriptが何を指すのかが曖昧なまま学習を進めている方は少なくないのではないでしょうか。

本記事では、ECMAScriptの定義やバージョン体系から、TypeScriptが言語としてどのような位置づけにあるのかまでを体系的に解説します。型システムの基本概念やコンパイルの仕組みも含め、TypeScriptの全体像を把握できる内容となっています。

JavaScriptとECMAScriptの関係

JavaScriptを書いている方なら、ECMAScriptという用語を一度は目にしたことがあるはずです。この2つの関係を正しく理解しておくことが、TypeScriptを学ぶ上での出発点となります。

ECMA-262規格とECMAScriptの定義

ECMAScriptとは、Ecma Internationalという情報通信関連技術の規格策定を行う標準化団体が策定する、ECMA-262という規格に基づいて標準化された言語のことです。

一般的にECMAScriptはJavaScriptの「言語規格」のように扱われることがあります。しかし厳密には、規格自体はECMA-262という仕様書に書かれているものです。ECMAScriptはその規格に則った言語そのものを指します。

以下の図は、ECMA-262規格とECMAScript、JavaScriptの関係を示しています。


flowchart TD
    A[Ecma International] -->|策定| B[ECMA-262 仕様書]
    B -->|定義する言語| C[ECMAScript]
    C -->|実装| D[JavaScript]
    C -->|実装| E[その他の実装]
    D -->|実行環境| F[ブラウザ]
    D -->|実行環境| G[Node.js]

この図のように、ECMA-262が仕様を定め、その仕様に基づく言語がECMAScriptです。JavaScriptはECMAScriptの代表的な実装にあたります。

ECMAScriptのバージョンと呼称

ECMA-262は1997年の初版から継続的に改定されてきました。主要なエディションの公開時期は次のとおりです。

エディション 公開時期
1 1997年6月
2 1998年6月
3 1999年12月
4 破棄
5 2009年12月
5.1 2011年6月
6(2015) 2015年6月
7(2016) 2016年6月
8(2017) 2017年6月
9(2018) 2018年6月
10(2019) 2019年6月
11(2020) 2020年6月

バージョンの呼び方には注意が必要です。バージョン5以前は「ES5」のように「ES+バージョン数」という形の通称を使います。一方、バージョン6以降は「ES2015」のように「ES+リリース年」という形の通称が一般的です。

ただし、ES2015に関してのみ、以前の流れを引き継いで「ES6」と表記されるケースもよく見られます。「ES2015」と「ES6」は同じものを指していますので、混乱しないようにしましょう。

ECMAScriptを意識するタイミング

日常的にJavaScriptを書くだけなら、ECMAScriptを意識する場面はそれほど多くありません。JavaScriptという言葉で代替できる場面がほとんどです。

しかし、実行環境による機能やシンタックスのサポートの差に触れるタイミングでは、ECMAScriptのバージョンを意識する必要が出てきます。たとえば、Google ChromeではClass構文を使うことができますが、Internet Explorerでは構文エラーとなってしまいます。

このように、同じJavaScriptでも実行環境によって使える機能にばらつきがあります。言語の種類について話すときは「JavaScript」で問題ありませんが、詳細な機能やシンタックスの話になると「ECMAScriptの○○バージョンの××」といった表現が必要になります。

コンパイラとECMAScriptのスーパーセット構造

実行環境ごとにサポートされる機能が異なるという問題は、コンパイラによって解決できます。さらに、ECMAScriptのバージョン間にはスーパーセットの関係があり、この理解がTypeScriptの位置づけを把握する鍵となります。

コンパイラとPolyfillの役割

ブラウザ上での動作を想定した場合、ES2020で書かれたスクリプトは古いブラウザでは実行時にエラーとなる可能性があります。かといって、最初からES5で書いていくのは現実的ではありません。

この問題を解決するのがコンパイラです。JavaScriptの文脈で出てくるコンパイラとは、「特定バージョンのECMAScriptを、任意の下位バージョンのECMAScriptに変換するための仕組み」のことを指します。

モダンなJavaScriptの開発現場では、ES2015以上の記法でソースコードを書き、Babelなどのツールを使って必要に応じてES5などのコードにコンパイルし、その結果を最終的なJavaScriptファイルとして扱うフローが一般的です。

以下の図は、コンパイルとPolyfillの役割の違いを示しています。


flowchart LR
    A[ES2015+の
ソースコード] --> B{変換対象の判定}
    B -->|シンタックス| C[コンパイラ
Babel等]
    B -->|存在しない機能| D[Polyfill]
    C --> E[アロー関数 →
function関数式]
    D --> F[Promise等の
機能を補完]
    E --> G[ES5で動作する
JavaScript]
    F --> G

この図のように、コンパイラはシンタックスの変換を担当し、Polyfillは下位バージョンに存在しなかった機能を補完します。

注意すべき点として、コンパイルで行われるのはあくまで「各種シンタックスをターゲットバージョンで有効なものに変換する」ことです。ES5をターゲットとした場合、ES2015以降で有効な「アロー関数式」はES5で解釈できる「function関数式」に変換されます。しかし、同じくES2015以降で有効な「Promise」はES5に代替可能なシンタックスがないため、そのまま変換されずに残ってしまいます。

このように、「そもそも存在していなかった機能」を任意の環境で動かすためには、コンパイルだけでなくPolyfillを使って対応する必要があります。

後方互換性とスーパーセットの関係

ECMAScriptの仕様を理解するときに重要なことが1つあります。それは、すべてのECMAScriptのバージョンが後方互換性を保っているという点です。

後方互換性を保っているとは、バージョンが上がった場合でも原則として使えなくなるシンタックスは出てこないということです。使えるシンタックスが上乗せされるだけとなります。ES2015ではES5のシンタックスをすべて使えますし、ES2020に関していえばES2019はもちろん、それ以前のすべてのバージョンのシンタックスを使うことができます。

この関係を踏まえると、特定のバージョンのECMAScriptは、それ以前のバージョンのECMAScriptのスーパーセットといえます。


flowchart BT
    A[ES5] -->|スーパーセット| B[ES2015 / ES6]
    B -->|スーパーセット| C[ES2016]
    C -->|スーパーセット| D[ES2017]
    D -->|スーパーセット| E[...]
    E -->|スーパーセット| F[ES2021]

    style A fill:#e8f4f8
    style F fill:#1a73e8,color:#fff

この図は、ECMAScriptの各バージョンが下位バージョンのスーパーセットとなっている関係を示しています。新しいバージョンは古いバージョンのすべての機能を含んだ上で、さらに新しい機能を追加しています。

TypeScriptにおける型システムの基礎

TypeScriptという言語を簡潔に表現すると、「ECMAScriptに対して静的型付けの機能を加えたスーパーセット」といえます。ここでは型とは何か、そして型システムがどのような役割を果たすのかを解説します。

データ型と型システムの概念

型とは正確にはデータ型と呼ばれるものです。プログラムには多くの種類の値が現れます。これらの値の種類を示して分類分けをするラベルのことを「データ型」と呼びます。

たとえば、0や1のような数値の値は「数値型」と分類されます。’hello’や’goodbye’のような文字列の値は「文字列型」と分類されます。

データ型が分類された値の特徴として「同じデータ型の値には同じ操作が可能になる」というものがあります。たとえば、JavaScriptのStringクラスにはtoUpperCaseというメソッドがあります。これは対象の文字列を大文字に変換するメソッドです。


console.log('hello'.toUpperCase()) // 'HELLO'

toUpperCaseは「文字列型」の値でのみ操作できるメソッドです。データ型という概念を持つことによって、「数値型」の値ではtoUpperCaseは呼べないという判断ができます。

型システムとはプログラムに存在する値をデータ型で分類して、そのプログラムが正しく振る舞うことを保証する機構のことをいいます。データ型からその処理が実行できるかどうかを判断する、この機構こそが「型システム」です。

静的型付けと動的型付けの違い

型システムは静的型付けと動的型付けの2種類に分けられます。プログラムの実行前に型検査を行うのが静的型付けです。プログラムを実行しながら型検査を行うのが動的型付けです。

TypeScriptは静的型付け言語であり、JavaScriptは動的型付け言語です。

動的型付け言語のJavaScriptでは、データ型としては呼べない1.toUpperCase()のような記述をしてブラウザで実行した場合、プログラムの実行時に型検査が行われます。その結果、TypeError(型エラー)が発生して処理が中断されてしまいます。

静的型付け言語の場合、コンパイラの段階で型検査を行うことで、このようなデータ型的に間違った処理が実行される前にエラーを発見できます。

TypeScriptでは、たとえば次のように : を使うことで変数に対してデータ型を明示できます。


const greeting: string = 'hello'

この宣言により、greetingには文字列型の値のみが代入可能になることがコンパイル時に保証されます。

ECMAScriptのスーパーセットとしてのTypeScript

ここまでECMAScriptについて説明してきた理由は、TypeScriptが特定のバージョンのECMAScriptのスーパーセットとみなせるからです。

先ほどの説明の通り、特定のバージョンのECMAScriptは下位バージョンのECMAScriptのスーパーセットとして見ることができます。たとえばES2015というプログラミング言語は、ES5が持っているすべてのシンタックスをカバーしつつ、それらに加えてClass構文などの新しい機能を持っている言語です。

そして実はTypeScriptに関しても同じことがいえます。TypeScriptというプログラミング言語は、特定のバージョンのECMAScriptのシンタックスをすべてカバーしつつ、さらにECMAScriptにはない「型システム」などの機能を持っている言語です。


flowchart BT
    A[ES5] -->|スーパーセット| B[ES2015]
    B -->|スーパーセット| C[ES2021]
    C -->|スーパーセット| D[TypeScript]

    D -.- E[型システム]
    D -.- F[インターフェース]
    D -.- G[ジェネリクス]

    style D fill:#3178c6,color:#fff
    style E fill:#f0f0f0
    style F fill:#f0f0f0
    style G fill:#f0f0f0

この図は、TypeScriptがECMAScriptの延長線上にあるスーパーセットとして位置づけられていることを示しています。

ここが非常に重要な部分です。TypeScriptという言語はECMAScriptとはまったく別の何かというわけではなく、ECMAScriptのスーパーセットです。ECMAScriptとして策定されている仕様は原則的にすべてTypeScriptでも使用できます。

TypeScriptと聞くと、単に「型システムのあるJavaScript」というものをイメージされる方が多いかもしれません。それは決して間違っていないのですが、「ECMAScriptのスーパーセット」という捉え方をしたほうが、TypeScriptという言語の立ち位置がクリアになるのではないでしょうか。

TypeScriptを導入するメリットとコンパイル環境

TypeScriptがECMAScriptのスーパーセットという点を踏まえた上で、TypeScriptを使うことで得られる具体的なメリットと、コンパイルに必要な環境について解説します。

安全なコード記述とエディタの補完機能

TypeScriptを選択する最も大きな動機となる機能が、型システムです。JavaScriptのプログラムを書く際、安全なコードを書くことは大きな課題のひとつです。コードが冗長化してしまう要因のひとつでもあります。

また、万が一コード内にバグがあった場合、エラーが発見されるのは実行時です。問題が発見されるのはデバッグ中、テスト中、あるいはユーザーが実行してはじめて発生してしまうケースもあるかもしれません。

TypeScriptでは、変数や関数の入出力、オブジェクトの構成などを型で制限でき、その正当性を機械的にチェックできます。入力が定義した型と異なる場合、コンパイル時点でエラーを検出できるので、コードのミスによる不具合を開発時に発見できます。実行時に発見するよりも安全かつ効率的に開発を進められます。

型システムがあることによって、開発時にもその恩恵を受けられます。Microsoft社のVisual Studio Code(以下、VS Code)に代表される、TypeScriptの入力支援機能を持ったテキストエディタでは、コード入力中に型定義から推論された候補による入力補完をしてくれます。安全なコードが書ける上に、コード入力自体の効率も大幅に改善できるでしょう。

導入コストの低さもメリットのひとつです。TypeScriptはJavaScriptのスーパーセットであり、また型システムも言語仕様として強制されたものではありません。既存のJavaScriptのコードをTypeScriptとしてコンパイルし実行しても、同じように動作します。普段JavaScriptを書いている人にとっては、基本的な記述なら比較的少ない学習コストで書き始められるでしょう。

tscコマンドとコンパイルの仕組み

TypeScriptは、そのままではブラウザやNode.jsでは実行できません。TypeScriptのコンパイラを使ってJavaScriptに変換する必要があります。

TypeScriptのファイルはJavaScriptのファイルとは区別され、拡張子は .ts を使います。JavaScriptの実行環境でこれを実行するために、.ts ファイルを .js に変換する手続きが必要です。

TypeScriptはnpmでパッケージとして提供されています。このパッケージをインストールすることで、TypeScriptをJavaScriptにコンパイルする機能を持った tsc コマンドと、エディタやIDE向けにTypeScriptの機能を提供するための tsserver コマンドを利用できるようになります。

tsc コマンドは主に次のような役割を果たします。

まず型チェックです。TypeScriptのコード内に型の誤りがないかを確認します。エラーがある状態でコンパイルを実行すると、誤りを検出してコンパイルは失敗となります。これにより、コードの誤りやミスを事前に発見できます。

次にECMAScript記法への変換です。型アノテーションはECMAScriptの記法ではないため、コンパイル時には削除されます。これにより、型情報を含んだTypeScriptのコードが、ブラウザやNode.jsで実行可能なJavaScriptに変換されます。


flowchart LR
    A[.tsファイル] --> B[tscコマンド]
    B --> C{型チェック}
    C -->|エラーなし| D[型アノテーション
の削除]
    C -->|エラーあり| E[コンパイル
エラー表示]
    D --> F[.jsファイル]

    style E fill:#ff6b6b,color:#fff
    style F fill:#48bb78,color:#fff

この図は、tscコマンドによるコンパイルの流れを示しています。まず型チェックが実行され、エラーがなければ型アノテーションが削除されてJavaScriptファイルが生成されます。

typescript – npm

Just a moment... www.npmjs.com

終わりに

本記事では、ECMAScriptの定義とバージョン体系から、TypeScriptが言語としてどのような位置づけにあるのかまでを解説しました。TypeScriptはECMAScriptとはまったく別の言語ではなく、ECMAScriptのスーパーセットとして設計された言語です。

TypeScriptのコードを読んでいく中で、どれがTypeScriptのシンタックスで、どれがECMAScriptのシンタックスなのかという点を意識してみると、コードを読む際の解像度が上がってくるでしょう。

型システムの基本、静的型付けと動的型付けの違い、そしてtscによるコンパイルの仕組みを理解した上で、実際にTypeScriptを書き始めてみてください。基礎を押さえることで、より効率的にTypeScriptの学習を進められるはずです。

コメントを残す