豆腐とコンソメ

豆腐とコンソメ

もろもろのプログラム勉強記録

Typescriptのtargetについて調べてみた

Typescriptでは、tsconfig.jsontargetを指定できる。
targetには、ES5だったり、ES6、ES2016と、ECMAScriptのバージョンを指定できる。

これは、TypescriptをコンパイルしてJavascriptに変換したときに、どのECMSScriptのバージョンにするかを指定できるオプション。

普段は、babelを使ってトランスパイルをしているんだけど、Typescriptであれば、babelを使う必要はなくなるんだと思う。

※とはいえ、既存のbabelを使ったプロジェクトにTypescriptを導入する際など、共存する方法はあるみたいだけれども。

さて、この素晴らしいtargetの機能だが試しに使ってみると、Typescriptに書くECMAScriptはどのバージョンでもいいんだっけ?とか、targetにES5を指定したら、Promiseでもなんでもどんとこいなの?とかいろんな疑問がでてくる。

ということで調べてみることにした。

結論

  • Typescriptはtargetに指定されたバージョンにあわせて、コードをトランスパイルしてくれる。
  • トランスパイルしてくれるのは、アロー関数だったり、クラス構文といった言語機能が対象となる。
  • Promiseなどの新しく定義されたオブジェクトや、既存のオブジェクトにメソッドが追加される場合の新しい機能(ランタイム機能)などはpolyfillを自分で追加する必要がある。
  • とはいえ、Typescriptに使いたいpolyfillを教えてあげないと、そんなオブジェクトorメソッドなんて知らねえと、コンパイル時に怒られるので、libオプションに使いたいpolyfillを教えてあげることが必要。
  • Promiseはちょっとややこしい。

※以下のstackoverflowの回答から言語系機能と、ランタイム機能という表現を引用しました。
https://stackoverflow.com/questions/51043439/using-latest-javascript-features-in-typescript-such-as-es2018

言語系機能とランタイム機能って具体的にどういうことよ、と思ったのですが、試しにコードをトランスパイルする機能を自分で作成することを想像するとちょっと腑に落ちた気がした。
Classだったり、アロー関数とかは構文解析することで、ES5の文法規則に乗っ取ってないからトランスパイルしなきゃ!ということはできそうな気がする。これが言語機能。
一方、オブジェクトにメソッドが存在しているかどうかは、実行される環境がそもそもそのオブジェクトが用意されているか等は実行時じゃないと判別がつかない。これがランタイム機能。
だから実行する環境を知っている作者が、個別にpolyfillを設定してねってことだと思う。

polyfillを試してみる

環境は以下の通り、Node.jsを使うことにする。

$ node -v
v10.15.0
$ yarn tsc --version
Version 3.6.3

ここでは、ES2019で追加されたArray.flatをTypescriptで使ってみるとどうなるかを見てみる。

ちなみに、Array.flatは自分の環境のv10.15.0では、そのまま使えない。

node.green

ES2019

まずは素直にtargetにES2019を指定する。

tsconfig.json

{
  "compilerOptions": {
    // 使うJSのバージョン
    "target": "ES2019" ,
  },
}

この状態で以下のコードをコンパイルしてみる。

index.ts

[[1, 2], 3, 4].flat()

コンパイルすると、以下のコードが生成される。

コンパイル後

// なにもかわってない
[[1, 2], 3, 4].flat()

このコードをNode.jsで実行すると

[[1, 2], 3, 4].flat();
               ^
TypeError: [[1,2],3,4].flat is not a function

flatというメソッドは存在していないので、そんな関数はないよと実行エラーになる。

ES2018

次に、flatが存在しないES2018を指定してコンパイルをしてみる。

tsconfig.json

{
  "compilerOptions": {
    // 使うJSのバージョン
    "target": "ES2018" ,
  },
}

こうすると、Arrayにflatなんてメソッドはないよってコンパイルエラーになる。

ここで試しに、libに設定を加えArrayにflatがあるんだよとTSに教えあげることにする。

tsconfig.json

{
  "compilerOptions": {
    // 使うJSのバージョン
    "target": "ES2018" ,
    // flatの定義があるライブラリを指定
     "lib": ["es2019.array"],
  },
}

指定できるlibは、追加したtypescriptのディレクトリlib配下で直接確認できる。

ちなみにlibのデフォルト値は、targetによってかわるみたい。

www.typescriptlang.org

For --target ES5: DOM,ES5,ScriptHost

libの設定をした後コンパイルを行うと、コンパイルが通った。
とはいえ、これはTypescriptにflatっていうメソッドがあるんだよと教えただけなので、コンパイル後のコードは以下の通りなんの変化もなくNode.jsで実行すると実行時エラーになる。

コンパイル後

// コードはそのまま
[[1, 2], 3, 4].flat()

polyfillを使う

ということで、polyfillを設定を行う。

pollyfillを提供してくれるcore-jsを追加して読み込む。

$ yarn add --dev core-js@3

index.ts

import "core-js"
[[1, 2], 3, 4].flat()

これをコンパイルすると、polyfillの定義が追加され、flatを使うことができた。
コンパイル後

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
require("core-js");
[[1, 2], 3, 4].flat();

不思議なPromise

当初はflatではなくPromiseを使ってtargetをes5にして試していたんだけど、不思議とこれはコンパイルエラーにならない。

targetがes5だから、libにはDOM,ES5,ScriptHostが指定されているはず。
ES5の仕様にPromiseはないはずなのに、なぜ、、、 と混乱を極めていたのですが、lib.es5.d.tsを覗いてみると、なんと、Promiseが定義されているじゃありませんか。

なぜ、lib.es5.d.tsにPromiseがあるんだろうかは、こちらのissueが上がっていました。
https://github.com/microsoft/TypeScript/issues/16132

が、明確な理由がわからず。誰か教えてください。