現象
Web向けのプロジェクトにNode.jsの型が混入し、setTimeout
などの型が壊れてビルドエラーになる
TS2322: Type 'Timeout' is not assignable to type 'number'.
これはライブラリをインストールした際、そのライブラリの型定義ファイルに@types/node
が含まれていてこのようなケースになることがあります
TypeScriptのissueに登録されていて、ワークアラウンドは提案されているものの、根本的な解決には至っていないようです
今回のゴール
- 開発中、ライブラリの型がわかる
- ビルドに失敗しない
対応策
tsconfig.json
のtypeRoots
で@types/node
の読み込み先を変えます
declare module 'node'
でNode.jsの型を上書きします
src/typings/node/index.ts
declare module 'node'
tsconfig.json
"typeRoots": ["src/typings", "node_modules/@types"],
このようにすることで、実際の@types/node
が読み込まれず、型が壊れること無くビルドができます
調査ログ
方針
@types/node
のany化
環境
- TypeScript: 3.7.4
- あるライブラリXXXの
XXX.d.ts
先頭のトリプルスラッシュディレクティブで@types/node
が読み込まれる
/// <reference types="node" /> type...
@types/node
がどのように読み込まれるのかを調べるため、traceResolution
を有効にしてビルドします
単純にライブラリをインストールした後
ビルド失敗
======== Module name 'XXX' was successfully resolved to 'node_modules/XXX/XXX.d.ts' with Package ID 'XXX/XXX.d.ts@x.y.z'. ======== ======== Resolving type reference directive 'node', containing file 'node_modules/XXX/XXX.d.ts', root directory 'node_modules/@types. ======== Resolving with primary search path 'node_modules/@types. Found 'package.json' at 'node_modules/@types/node/package.json'.
ライブラリの型を読み込んだ後、node
を探して無事に見つかって@types/node
が読み込まれていますね😇
paths
で@types/node
の読み込み先を変える
ビルド失敗
tsconfig.json
"paths": { "node": ["typings/node.d.ts"] },
src/typings/node.d.ts
declare module 'node'
======== Module name 'XXX' was successfully resolved to 'node_modules/XXX/XXX.d.ts' with Package ID 'XXX/XXX.d.ts@x.y.z'. ======== ======== Resolving type reference directive 'node', containing file 'node_modules/XXX/XXX.d.ts', root directory 'node_modules/@types. ======== Resolving with primary search path 'node_modules/@types. Found 'package.json' at 'node_modules/@types/node/package.json'.
変わらないですね…
typeRoots
に定義
ビルド失敗
tsconfig.json
"typeRoots": ["src/typings", "node_modules/@types"],
======== Module name 'XXX' was successfully resolved to 'node_modules/XXX/XXX.d.ts' with Package ID 'XXX/XXX.d.ts@x.y.z'. ======== ======== Resolving type reference directive 'node', containing file 'node_modules/XXX/XXX.d.ts', root directory 'src/typings, node_modules/@types'. ======== Resolving with primary search path 'src/typings, node_modules/@types'. Found 'package.json' at 'node_modules/@types/node/package.json'.
無事に@types/node
を見つけてしまいましたね😇
typeRoots
からnode_modules/@types
を消すとどうなるでしょうか
Resolving with primary search path 'src/typings'. Looking up in 'node_modules' folder, initial location 'node_modules/XXX'. File 'node_modules/XXX/node_modules/node.d.ts' does not exist. (中略)... ======== Type reference directive 'node' was successfully resolved to 'node_modules/XXX/node_modules/@types/node/ts3.2/index.d.ts' with Package ID '@types/node/ts3.2/index.d.ts@12.12.35', primary: false. ========
src/typings
からは見つからず、ライブラリ内のnode_modules
から見つけましたね😇
typeRoots
に指定しているディレクトリの構造をnode_modules/@types
以下に揃える
ビルド成功
node_modules/@types
以下を見ていると、ライブラリ名ごとにディレクトリが別れていると思いました
なので、typeRoots
に指定したディレクトリも同じようにライブラリ名でディレクトリを作ってみます
src/typings/node/index.ts
declare module 'node'
tsconfig.json
"typeRoots": ["src/typings", "node_modules/@types"],
======== Module name 'XXX' was successfully resolved to 'node_modules/XXX/XXX.d.ts' with Package ID 'XXX/XXX.d.ts@x.y.z'. ======== ======== Resolving type reference directive 'node', containing file 'node_modules/XXX/XXX.d.ts', root directory 'src/typings, node_modules/@types'. ======== File 'src/typings/node/package.json' does not exist. File 'src/typings/node/index.d.ts' exist - use it as a name resolution result. Resolving real path for 'src/typings/node/index.d.ts', result 'src/typings/node/index.d.ts'. ======== Type reference directive 'node' was successfully resolved to 'src/typings/node/index.d.ts', primary: true. ========
自分で定義した方の'node'の型を読み込んでくれた!🎉
おまけ create react appで@types/node
が入った場合どうなるのか
--template typescript
でTypeScriptを入れます
このときのtsconfig.json
には"skipLibCheck": true
が入っています
"skipLibCheck": true
を消してビルドしてみると…?
$ react-scripts build The following changes are being made to your tsconfig.json file: - compilerOptions.skipLibCheck to be suggested value: true (this can be changed)
自動的にtrue
になりました。強い。
参考
以下の記事でtypeRoots
がトリプルスラッシュディレクティブにしか効かないことを読んで、
typeRoots
を使って読み込み先を変える方法を思いつきました