できない.dev

TypeScript の tsconfig.json paths が実行時に解決できない

tsc は paths を「型解決」専用に使い、コンパイル後の JS にエイリアスを書き換えない。
Node 直接実行や ts-node では tsconfig-paths などのローダ、bundler では別途 alias 設定が必要。

#tsconfig#paths#alias#baseUrl#ts-node

公開:

要約

tsconfig.jsonpathstsc が型を解決するためだけのマッピング で、コンパイル後の JS は import "@/foo" のまま残る。
Node がそのパスを知らなければ実行時に Cannot find module になる。
実行系(ts-node / node / bundler)ごとに 同じ alias を別途設定する 必要がある。

よくある原因

  1. paths は型専用: tsc 単体ではコンパイル時にパス書き換えをしない(公式の明記
  2. ts-node でローダ未指定: そのままでは paths を解釈できず、@/foo を node_modules で探して失敗する
  3. bundler 側未設定: webpack / Vite / esbuild はそれぞれ独自の alias を持ち、tsconfig の paths を自動採用しない
  4. 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.ts

tsconfig-paths/registerpaths を読んで 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 と一致するマッピング を定義する。

この記事は役立ちましたか?