TypeScript の tsconfig.json paths が実行時に解決できない
tsc は paths を「型解決」専用に使い、コンパイル後の JS にエイリアスを書き換えない。
Node 直接実行や ts-node では tsconfig-paths などのローダ、bundler では別途 alias 設定が必要。
#tsconfig#paths#alias#baseUrl#ts-node
公開:
要約
tsconfig.json の paths は tsc が型を解決するためだけのマッピング で、コンパイル後の JS は import "@/foo" のまま残る。
Node がそのパスを知らなければ実行時に Cannot find module になる。
実行系(ts-node / node / bundler)ごとに 同じ alias を別途設定する 必要がある。
よくある原因
- paths は型専用: tsc 単体ではコンパイル時にパス書き換えをしない(公式の明記)
- ts-node でローダ未指定: そのままでは paths を解釈できず、
@/fooを node_modules で探して失敗する - bundler 側未設定: webpack / Vite / esbuild はそれぞれ独自の
aliasを持ち、tsconfig の paths を自動採用しない - baseUrl 欠落:
pathsのキー解決にはbaseUrlが必要
解決策
1. tsconfig 側の最低構成
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@/*": ["*"]
}
}
}baseUrl が無いと paths のキーが解決されない。
2. ts-node で実行する
npm install -D ts-node tsconfig-paths
node -r ts-node/register -r tsconfig-paths/register src/main.tstsconfig-paths/register が paths を読んで Node の解決経路に合流させる。ts-node の代わりに tsx を使う場合も同様の前処理が必要。
3. tsc 出力を実体パスに書き換える
npm install -D tsc-alias
npx tsc && npx tsc-aliasビルド成果物の import "@/foo" を相対パスに置換する。
production 配布時はこちらが安全。
4. bundler の alias を合わせる
// vite.config.ts
import { defineConfig } from "vite";
import path from "node:path";
export default defineConfig({
resolve: {
alias: { "@": path.resolve(__dirname, "src") }
}
});webpack / esbuild も同様に tsconfig の paths と一致するマッピング を定義する。