Supporting lib from node_modules

TL;DR

DOMの型定義 lib.dom.d.ts を差し替えできるようになった。

lib.dom.d.tsとは何ぞ?

DOM APIの型定義が書いてあるファイル。

  • setTimeout

  • window

  • fetch

イメージ的には「グローバルに生えているAPIの型定義」に近い。

TypeScriptのコード
setTimeout(() => {
  window.alert("");
  fetch("https://foo.com");
}, 1000);
lib.dom.d.tsから抜粋
// setTimeout,window,fetchなどこのファイルで型定義されている。
declare function setTimeout(...): number;

interface Window extends ... {
  alert(message?: any): void;
  ...
}

interface WindowOrWorkerGlobalScope {
  fetch(...): Promise<Response>;
}

lib.dom.d.tsはどこにある?

TypeScriptをインストールした時に一緒にくっついてくる。 node_modulesフォルダを覗くとファイルとして存在している。

"dependencies": {
  "typescript": "4.1.5",
}

node_modulesフォルダにはlib.dom.d.tsの他に「lib.es2015.object.d.ts」や「lib.es2015.promise.d.ts」などの「d.ts」ファイルが存在している。 これらはtsconfig.jsonの lib 指定で「どのファイルを読み込むか」を指定できる。

// tsconfig.json
"lib": ["dom", "es2015"]

// node_modules/@typescriptのこれらが読み込まれる
lib.dom.d.ts
lib.es2015.object.d.ts
lib.es2015.promise.d.ts
// tsconfig.json
"lib": ["dom", "es2020"]

// node_modules/@typescriptのこれらが読み込まれる
lib.dom.d.ts
lib.es2020.object.d.ts
lib.es2020.promise.d.ts

es2015es2020 はECMAScriptのバージョンを示している。 ECMA2020で新しいAPIが追加されるとTypeScriptが es2020 のファイル群を追加し、そこに新しいAPIの型定義が書かれる。

例えばECMA2022で追加された Object.hasOwn というAPIがある。 lib.es2022.object.d.tsを見ると、hasOwnの型定義が追加されたことが分かる。

https://github.com/microsoft/TypeScript/blob/main/lib/lib.es2022.object.d.ts

// lib.es2022.object.d.ts
interface ObjectConstructor {
  ...
  hasOwn(o: object, v: PropertyKey): boolean;
}

lib:["es2022"] を指定すればlib.es2022.object.d.tsが読み込まれ Object.hasOwn の型が解決できる。それ以前の lib:["es2015"] など指定している場合、TypeScriptは Object.hasOwn の型を見つけられず型定義エラーになる。

ここで、もう一度node_modulesフォルダの下を覗いてみよう。

lib.dom.d.ts
lib.es2015.object.d.ts
lib.es2015.promise.d.ts
lib.es2015.iterable.d.ts
lib.es2022.object.d.ts
lib.es2022.promise.d.ts
...

dom にはバージョン番号がなく、ひとつのファイルしか存在しない。

「グローバルに生えているAPI」を定義したlib.dom.d.tsはどこでECMAScriptのバージョン指定ができるのか?

グローバルに生えた新しいAPIが使えない?

そういえば最近 structuredClone を使いたかったけど型の未定義エラーになってしまったので、これを追いかけてみる。

Edit structured-clone-ts4.1

"lib.dom.d.ts" v4.1.5

structuredClone を使って Cannot find name エラーになったv4.1.5。グローバルっぽいオブジェクトを探しても、確かに定義されていない。

"lib.dom.d.ts" v4.7.4

structuredClone があった。

TypeScriptそのものをアップデート(従来)

TypeScript v4.7に structuredClone が存在するなら、そのバージョンをインストールすればいいことになる。

npm install typescript@4.7

// lib.dom.tsがv4.7にアップデートされる
// ここにはstructuredCloneが定義されている

実際には、フレームワークだったり他のパッケージとの兼ね合いでそんなに気軽にTypeScriptのバージョンは上げられない。ほんの一部分の要求のために、プロジェクトすべてに影響する解決策をとるのはリスクが高い。

lib.dom.d.tsをアップデート(新)

TypeScript v4.5から TypeScriptのバージョンを上げずにlib.dom.d.tsだけ差し替える ことができるようになった。

オフィシャルでメンテナンスされている「@types/web」パッケージをインストールすることで任意のlib.dom.d.tsをプロジェクトに取り込むことができる。

https://www.npmjs.com/package/@types/web

「@types/web」の2022/10時点の最新リリースはv0.0.75だった。

https://github.com/microsoft/TypeScript-DOM-lib-generator/releases/tag/%40types%2Fweb%400.0.75

ソースを追いかけると...。あった structuredClone が入っている。

という訳で「@types/web」のv0.0.75をインストール。

"dependencies": {
  "@typescript/lib-dom": "npm:@types/web@0.0.75"
}

グローバルに生えた新しいAPIが使えるようになった

structuredClone の型定義が見つかるようになり、コンパイルエラーは出なくなった。

Edit structured-clone-ts4.1-types

2つのlib.dom.d.ts

TypeScriptに同梱されたlib.dom.d.tsと「@types/web」に同梱されたlib.dom.d.ts。 ローカルのプロジェクトには2つのlib.dom.d.tsが存在する。

// node_modules/@typescript
lib.dom.d.ts
lib.es2015.object.d.ts
lib.es2015.string.d.ts
lib.es2020.object.d.ts
lib.es2020.string.d.ts
...
// node_modules/@types/web
lib.dom.d.ts

「@types/web」が存在する場合、このフォルダのlib.dom.d.tsが読み込まれる。 これが今回のアップデート「Supporting lib from node_modules」で追加された機能になる。

Last updated