0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

tsconfig の target と lib の違いを具体例で理解する

Last updated at Posted at 2025-03-18

はじめに

TypeScript の tsconfig.json では、targetlib"es2015", "es2020", "esnext" などの ECMAScript バージョンを指定できます。しかし、これらの違いを明確に理解するのは意外と難しく、私自身も混乱したことがありました。個人的には特に lib の存在意義がなかなか理解できませんでした。

この記事では、targetlib の違いをより分かりやすく説明するために、実際に手を動かしながら検証していきます。

検証環境の用意

特に特殊なことはしてません。自分で用意できる方、あるいは検証する必要がない方は読み飛ばしてください。

今回の検証には Node.js 16 を使用します。(本当はもっと古いバージョンを使いたかったのですが、セットアップが謎に大変で断念)
Node.js 16 は ECMAScript 2021 までをサポートしています。

まずは、お手元の環境に Node.js 16 をインストールしてほしいのですが、Docker を使用すれば手軽に準備できます。
検証用のディレクトリを作成し、以下の Dockerfiledevcontainer.json を用意して、VS Code から devcontainer を開いてください。

Dockerfile
FROM node:16.0.0
CMD ["sleep", "infinity"]
.devcontainer/devcontainer.json
{
  "name": "Development Container",
  "build": {
    "dockerfile": "../Dockerfile",
    "context": ".."
  }
}

Node.js 16 が動作する環境を用意できたら、まずは package.json を生成します。

npm init -y

次に、TypeScript をインストールします。

npm install -D typescript

最後に、本稿の主役である tsconfig.json を生成します。

npx tsc --init

なお、トランスパイル後のファイルの出力先として、tsconfig.jsonoutDir"./out" に設定しておきます。

tsconfig.json
{
    "compilerOptions": {
        ...
        "outDir": "./out"
        ...
    }
}

重要ポイント:target と lib は対象が異なる

違いを理解するうえで一番重要なポイントを最初に抑えておきます。
targetlib は、それぞれ以下の異なる要素を対象としています。

  • target: 構文(シンタックス) に関する設定
  • lib: API(標準組み込みオブジェクトやメソッド) に関する設定

ここで言う構文とは、例えば constlet、アロー関数 () => {}、クラスの static ブロックなどが該当します。

一方で、API に関しては Map などの標準組み込みオブジェクトや、Array.at のようなメソッドが該当します。

target と構文の関係

target は、TypeScript のコードをJavaScriptにトランスパイルする際の 構文の変換ルール を決定します。

その具体的な例として、クラスの static ブロックを取り上げます。 static ブロックは ECMAScript 2022 で導入された構文であり、Node.js 16(ECMAScript 2021 までサポート)では使用できません。

実際に、以下のような JavaScript コード syntax.js を作成し、 node syntax.js で実行してみましょう。ランタイムエラーが発生するはずです。

syntax.js
class Example {
    static var = "foo"
    static {
        this.var = "bar"
    }
}

console.log(Example.var)

続いて、この問題が TypeScript のトランスパイルにより解決する様子を見ていきます。

まずは syntax.ts を作成し、syntax.js と同じコードを記述します。Node.js 16 で動作させるべく、 tsconfig.jsontarget"es2021" に設定してトランスパイルしましょう。

npx tsc

出力された out/syntax.js を確認すると、static ブロックが別の構文に変換されていることがわかります。

out/syntax.js
"use strict";
var Example = /** @class */ (function () {
    function Example() {
    }
    var _a;
    _a = Example;
    Example.var = "foo";
    (function () {
        _a.var = "bar";
    })();
    return Example;
}());
console.log(Example.var);

このトランスパイル後のファイルを node out/syntax.js で実行すると、エラーなく動作します。

このように、lib に所望の ECMAScript のバージョンを指定することで、そのバージョンで動作するように変換されるわけです。

lib と API の関係

lib は、TypeScript の 型チェック 時に影響します。その例として、Array.at を取り上げます。

このメソッドは ECMAScript 2022 で追加されました。したがって、lib"es2021" を指定した場合、TypeScript の型チェック時に Array.at が存在しないため、エラーが発生します。

型チェックエラーの確認

まず、api.ts を作成し、以下のコードを記述します。

api.ts
const arr = [1, 2, 3];
console.log(arr.at(-1));

次に、tsconfig.jsonlib"es2021" に設定し、npx tsc を実行してみましょう。
("DOM" も指定しないと、console.log() 等の標準APIで型チェックエラーが発生するので指定しておきましょう)

tsconfig.json
{
    "compilerOptions": {
        ...
        "lib": ["es2021", "DOM"]
        ...
    }
}

次のような型チェックエラーが発生するはずです。

error TS2550: Property 'at' does not exist on type 'number[]'.

そこで、lib"es2022" に変更して再度 npx tsc を実行してみましょう。エラーは解消され、トランスパイルが成功するはずです。

しかし、重要なのはここからです。

出力された out/api.js を確認すると、Array.at は変換されずにそのまま残っています。

out/api.js
"use strict";
var array = [0, 1, 2];
console.log(array.at(2));

繰り返しますが、Node.js 16 では "es2021"までしかサポートしていないため、Array.at はサポートされていません。したがって、node out/api.js を実行するとランタイムエラーが発生します。

指定バージョンで動くように構文変換してくれる target のバージョン指定とは違い、lib は何の変換にも関与しません。あくまでも型チェックで参照する型ファイルのバージョンを指定できるだけです。

libtarget を別々に設定できる理由

Node.js 16 で動作させるために、target には es2021 を指定しました。一方で、Array.at を使うため(型チェックを通すため)に、lib には es2022 を指定しました。

「どうせランタイムエラーになるのなら、libtarget 以上のバージョンを指定する意味はあるのか?」という疑問が生じるかもしれません。しかし、libtarget に別のバージョンを設定できるようになっているのにはちゃんと理由があります。

例えば、実行環境が対応していないと分かりつつも、スマートな書き味を求めて新しい API でコードを書きたい!というケースはあるでしょう。先ほど例に上げた Array.at(-1) を使えば配列の末尾の要素を簡単に取得できます。libes2022 を指定しておけば、Typescriptでコードを書いている分には問題なくそのAPIが使えるわけです。

とはいえ何度も言うように、実行環境がそのAPIをサポートしていなければランタイムエラーになってしまいます。
したがって、このようなケースにおいては、何らかの方法でこのランタイムエラーを解決する必要があります。

Polyfillでランタイムエラーを回避する

本稿の例では Array.at のランタイムエラーを取り上げてきました。これを解決するには、何らかの方法で Array.at の実装を差し込んであげる必要があります。このようにギャップを埋めるために差し込むコードを Polyfill と言います。

自分で実装しても良いのですが、core-js を使用すると簡単です。

まずはパッケージをインストールします。

npm install core-js

続いて api.tsimport 'core-js/actual/array/at'; を追加します。

api.tsx
import 'core-js/actual/array/at';
const arr = [1, 2, 3];
console.log(arr.at(-1));

そして npx tsc を実行し、トランスパイル後のコードを確認してみましょう。

out/api.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
require("core-js/actual/array/at");
var array = [0, 1, 2];
console.log(array.at(2));

インストールしたパッケージが import(本例ではrequireですが)されていることが分かります。実際のコードはこんな感じ

Polyfill を差し込んだおknode out/api.js を実行してみると正常に動作するはずです。

まとめ

TypeScript の target はどのバージョンの構文(シンタックス) に変換するかを決定し、lib型チェックの基準となる API を決定します。target を古く、lib を新しく設定することで開発時の利便性が向上しますが、実行時には Polyfill が必要になります。

参考

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?