はじめに
Python ブームが去って React ブームが再燃している @guppy0356 です。
個人開発で Hono や React を触っていると、ディレクトリ構成にこだわりたくなりますよね。
「深い階層から ../../ でインポートするのは辛い! Next.js みたいに @/ でインポートしたい!」
そう思って tsconfig.json を設定したのに、Vite や Vitest が動かずにハマってしまいました。
今回はその解決策と、「なぜ3つのファイルすべてに設定が必要なのか?」 という仕組みの部分を整理しました ![]()
技術スタック
- Vite
- Vitest
- TypeScript
- node v20.19.5
やりたかったこと
src ディレクトリ配下を @ というエイリアス(別名)で参照したい。
// 理想
import { Button } from "@/components/Button";
// 現実
import { Button } from "../../components/Button";
1. 発生した問題
まず、TypeScript にエイリアスを教えるために tsconfig.json に paths を追記しました。
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
これで VS Code 上の赤い波線は消え、インテリセンス(補完)も効くようになりました。
しかし、いざ実行してみると……。
-
npm run dev(Vite) → エラーで起動しない -
npm run test(Vitest) → テストが落ちる
なぜ tsconfig.json に書いたのに動かないのでしょうか?
2. 実験と考察:各設定ファイルの役割
この挙動を理解するために、設定ファイルの役割と、設定が欠けたときの挙動を表にまとめました。
実験結果の解釈
| 設定ファイル | 役割 | 設定がないとどうなる? | エディタ上のエラー | 実行時の動作 |
|---|---|---|---|---|
| tsconfig.json | 型チェック & DX | ✅ 開発鯖・テストは動く | ❌ 型エラーが出る | 問題なし |
| vite.config.ts | バンドル & 開発鯖 | ❌ 開発サーバーが起動しない | - | エラー |
| vitest.config.ts | テスト実行 | ❌ テストが失敗する | - | エラー |
各ファイルの責務
このように整理すると、それぞれのファイルが全く別のタイミングで仕事をしていることがわかります。
1. tsconfig.json の paths
- 目的: 型チェックと開発体験(DX)
-
使用される場面:
- エディタ(VS Code)の Language Server による補完・定義ジャンプ
-
tscコマンドによる型検証
- ポイント: これはあくまで「静的な解析」用であり、実際のコード実行(ランタイム)には関与しません。
2. vite.config.ts の resolve.alias
- 目的: 開発サーバーとビルド時のファイル解決
-
使用される場面:
npm run dev,npm run build -
ポイント: ブラウザが理解できる形にファイルを束ねる(バンドルする)際に、
@がどこにあるかを知る必要があります。
3. vitest.config.ts の resolve.alias
- 目的: テスト実行時のファイル解決
-
使用される場面:
npm test - ポイント: Vitest はテスト実行のために独自のインスタンスを立ち上げるため、Vite とは別に(あるいは連動して)パス解決のルールを知る必要があります。
結論
tsconfig.json は「コードを書くとき」のため、vite.config.ts と vitest.config.ts は「コードを実行するとき」のために必要です。
これらすべてが揃って初めて、開発・テスト・エディタの全方位で @/ エイリアスが機能します。
3. 解決策:vite-tsconfig-paths を使う
それぞれのファイルに alias を手書きするのは二重管理になりメンテナンス性が悪いです。
そこで、vite-tsconfig-paths というプラグインを使って、tsconfig.json の設定を正(Single Source of Truth)にします。
インストール
npm install -D vite-tsconfig-paths
vite.config.ts の修正
プラグインを読み込ませます。これで Vite 側が tsconfig.json の paths を理解するようになります。
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths'; // 追加
export default defineConfig({
plugins: [
react(),
tsconfigPaths(), // 追加
],
});
vitest.config.ts の修正
テスト設定が分かれている場合、こちらにも忘れずに追加します。
import { defineConfig } from 'vitest/config';
import tsconfigPaths from 'vite-tsconfig-paths'; // 追加
export default defineConfig({
plugins: [
tsconfigPaths(), // ここにも必要!
],
test: {
environment: 'jsdom',
},
});
まとめ
-
tsconfig.jsonだけでは、実行時(Vite/Vitest)にパスが解決できない。 - かといって3箇所に同じパスを書くのは大変。
-
vite-tsconfig-pathsを導入して、tsconfig.jsonを一元管理の親にするのがベストプラクティス!
仕組みがわかると、エラーが出たときに「あ、これはエディタの設定不足だな」「これはバンドラの設定不足だな」と切り分けができるようになりますね。