- TypeScript
TypeScript pathsエイリアス設定|@/が解決できない原因と直し方
TypeScript の paths とは、@/lib/greet のような短い名前を実際のファイルの場所に対応付ける tsconfig.json のオプションです。 ただし paths が効くのは tsc の型チェックと、tsconfig.json を読むツールだけです。Vite の開発サーバーや Vitest、Node.js には、それぞれ別の設定が必要になります。「エディタでは通るのに実行すると見つからない」という状況は、ほとんどがこの違いから起こります。
この記事では、TypeScript 7.0 で baseUrl が使えなくなったあとの paths の書き方を、6.0 の警告全文と 7.0 のエラー全文つきで示します。そのうえで、同じ @/ の import を tsc・Vite・Vitest・Node.js の直接実行・tsx で実際に動かし、どのツールで何が必要かを表にまとめました。package.json の imports で設定を1か所にまとめる方法も、実行結果つきで載せています。
確認環境:Ubuntu 24.04 / Node.js 22.22.2(一部 24.21.0)/ TypeScript 7.0.2(比較に 6.0.3・5.9.3)/ Vite 8.3.2 / Vitest 5.0.3 / tsx 4.23.15 / vite-tsconfig-paths 6.1.1。
pathsエイリアスの早見表
TypeScript 7.0でのpathsの書き方
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
baseUrl は書かず、対応先を ./ から始めるのがポイントです。この書き方は 5.9.3・6.0.3・7.0.2 のどれでもエラー0件でした。
ツール別・必要な設定
tsconfig.json に上の paths だけを書いた状態で、import { greet } from "@/lib/greet"; が解決できるかを確かめた結果です。
| ツール | paths だけで解決できるか | 出たエラー | 必要な設定 |
|---|---|---|---|
| tsc(型チェック) | できる | paths が無いと TS2307 | tsconfig.json の paths |
| tsc で出力した JS を node で実行 | できない | ERR_MODULE_NOT_FOUND | package.json の imports に切り替える |
| Vite 開発サーバー | できない | Failed to resolve import | vite.config の resolve.tsconfigPaths: true(または resolve.alias) |
Vite ビルド(vite build) |
できた(8.3.2 での結果) | なし | 開発サーバーと同じ設定を書いてそろえる |
| Vitest | できない | Cannot find package | vite.config に Vite と同じ設定 |
| Node.js で .ts を直接実行 | できない | ERR_MODULE_NOT_FOUND | package.json の imports |
| tsx | できる | paths が無いと ERR_MODULE_NOT_FOUND | 追加の設定なし(tsconfig.json を読む) |
エラー文から原因を引く表
| 表示されたエラー | 出すツール | 原因 | 直し方 |
|---|---|---|---|
error TS5102: Option 'baseUrl' has been removed. |
tsc 7.0 | tsconfig.json に baseUrl がある |
baseUrl を消し、paths の対応先を ./ から書く |
error TS5090: Non-relative paths are not allowed. |
tsc | baseUrl なしで "src/*" と書いている |
"./src/*" に直す |
error TS2307: Cannot find module '@/lib/greet' or its corresponding type declarations. |
tsc | paths が無い、パターンが合わない、継承先で上書きされた | --showConfig と --traceResolution で確認 |
Failed to resolve import "@/lib/greet" from "src/main.ts". Does the file exist? |
Vite 開発サーバー | Vite 側の設定が無い | resolve.tsconfigPaths: true |
Error: Cannot find package '@/lib/greet' imported from … |
Vitest | 同上 | vite.config(または vitest.config)に同じ設定 |
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@/lib' imported from … |
Node.js | Node.js は tsconfig.json を読まない | imports に切り替える、または tsx で実行 |
TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module "#/lib/greet.ts" is not a valid internal imports specifier name |
Node.js 22 | #/ で始まる imports に対応していない版 |
#src/* のように名前を付ける。または Node.js 24 を使う |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「TypeScript pathsエイリアス設定|@/が解決できない原因と直し方」
検証に使うプロジェクト
alias-demo/
├── package.json ("type": "module")
├── tsconfig.json
├── index.html (Vite 用。/src/main.ts を読み込む)
└── src/
├── main.ts
└── lib/
├── greet.ts
└── greet.test.ts
// src/lib/greet.ts
export function greet(name: string): string {
return `こんにちは、${name}さん`;
}
// src/main.ts
import { greet } from "@/lib/greet";
console.log(greet("taro"));
以降のエラー文に出るフォルダのパスは、/home/taro/alias-demo に置き換えて表示しています。
TypeScript 7.0でbaseUrlが使えなくなった
以前の版でよく使われていた、baseUrl と組み合わせた書き方から始めます。
{
"compilerOptions": {
"target": "es2023",
"module": "esnext",
"moduleResolution": "bundler",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"noEmit": true
},
"include": ["src"]
}
この tsconfig.json を3つの版にかけた結果です。
| 版 | 結果 |
|---|---|
| 5.9.3 | エラーなし(終了コード 0) |
| 6.0.3 | TS5101(予告のエラー。終了コード 2) |
| 7.0.2 | TS5102 と TS5090 |
TypeScript 6.0.3 の出力全文です。
tsconfig.json(6,5): error TS5101: Option 'baseUrl' is deprecated and will stop functioning in TypeScript 7.0. Specify compilerOption '"ignoreDeprecations": "6.0"' to silence this error.
Visit https://aka.ms/ts6 for migration information.
TypeScript 7.0.2 の出力全文です。
tsconfig.json(6,5): error TS5102: Option 'baseUrl' has been removed. Please remove it from your configuration.
Use '"paths": {"*": ["./*"]}' instead.
tsconfig.json(8,15): error TS5090: Non-relative paths are not allowed. Did you forget a leading './'?
6.0.3 では、メッセージのとおり "ignoreDeprecations": "6.0" を足すとエラーが消えました。ただし同じ設定を 7.0.2 にかけると TS5102 と TS5090 がそのまま出るため、これは 6.0 の間だけ使える一時的な回避です。
直し方:baseUrlを消して./から書く
baseUrl の行を消しただけでは、まだ1件残ります。
tsconfig.json(7,15): error TS5090: Non-relative paths are not allowed. Did you forget a leading './'?
"src/*" を "./src/*" に直すと0件になります。paths の対応先は、その paths を書いた tsconfig.json のあるフォルダからの相対パスとして扱われます。
{
"compilerOptions": {
"target": "es2023",
"module": "esnext",
"moduleResolution": "bundler",
"paths": {
"@/*": ["./src/*"]
},
"noEmit": true
},
"include": ["src"]
}
| 直す前 | 直した後 |
|---|---|
"baseUrl": "." と "@/*": ["src/*"] |
baseUrl なしで "@/*": ["./src/*"] |
"baseUrl": "./src" と "@/*": ["*"] |
baseUrl なしで "@/*": ["./src/*"] |
baseUrl だけを書き、import "lib/greet" のように読む |
7.0.2 のメッセージが示す "paths": {"*": ["./*"]} を足す |
TypeScript 6.0 のリリースノートでも、baseUrl はモジュール解決の起点として扱われなくなること、baseUrl に書いていた部分を paths の各項目へ移すことが案内されています。
tscで@/が解決できないとき(TS2307)
paths を消すと、tsc は次のエラーを出します。
src/main.ts(1,23): error TS2307: Cannot find module '@/lib/greet' or its corresponding type declarations.
npx tsc --locale ja での表示は次のとおりです。
src/main.ts(1,23): error TS2307: モジュール '@/lib/greet' またはそれに対応する型宣言が見つかりません。
paths を書いているのに TS2307 が出る場合に、確かめる点は4つです。
1. パターンの両側に*があるか
"@/*": ["./src"] のように対応先の * を落とすと、同じ TS2307 になりました。キーと対応先の両方に * を書きます。
2. 実際に使われている設定を--showConfigで見る
npx tsc --showConfig
extends で設定を継承している場合、子の tsconfig.json に paths を書くと、親の paths は丸ごと置き換わります(項目ごとの合成はされません)。親に "@/*"、子に "~/*" を書いた構成で確かめると、--showConfig の paths は子の "~/*" だけになり、@/lib/greet は TS2307 になりました。子にも "@/*" を書けば解消します。
3. どこを探したかを--traceResolutionで見る
npx tsc --traceResolution | grep -A7 "Resolving module '@/lib/greet'"
======== Resolving module '@/lib/greet' from '/home/taro/alias-demo/src/main.ts'. ========
Explicitly specified module resolution kind: 'Bundler'.
Resolving in CJS mode with conditions 'import', 'types'.
'paths' option is specified, looking for a pattern to match module name '@/lib/greet'.
Module name '@/lib/greet', matched pattern '@/*'.
Trying substitution './src/*', candidate module location: './src/lib/greet'.
Loading module as file / folder, candidate module location '/home/taro/alias-demo/src/lib/greet', target file types: TypeScript, JavaScript, Declaration, JSON.
File '/home/taro/alias-demo/src/lib/greet.ts' exists - use it as a name resolution result.
「matched pattern」の行が出ていなければパターンが合っていません。「candidate module location」のパスが想定と違えば、対応先の相対パスの起点がずれています。親の tsconfig.json に書いた "./src/*" は、子のフォルダではなく親のフォルダの src を指していました。
4. tscにファイル名を渡していないか
npx tsc --noEmit src/main.ts のようにファイル名を渡すと、tsconfig.json は読まれません。7.0.2 では、この実行方法そのものが次のエラーになります。
error TS5112: tsconfig.json is present but will not be loaded if files are specified on commandline. Use '--ignoreConfig' to skip this error.
paths を効かせるには、ファイル名を付けずに npx tsc(または npx tsc -p tsconfig.json)で実行します。
tscはimportを書き換えない
paths を設定して tsc で JavaScript を出力しても、import の文字列はそのまま残ります。rootDir と outDir を足した設定で出力した dist/main.js です。
import { greet } from "@/lib/greet";
console.log(greet("taro"));
これを node dist/main.js で実行した結果です。
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@/lib' imported from /home/taro/alias-demo/dist/main.js
Node.js は @/lib を「@/lib という名前の npm パッケージ」として node_modules から探し、見つからずに止まっています。paths は tsc に場所を教えるだけの設定で、実行時の解決は実行する側の仕事です。Node.js でそのまま動かす場合は、後半の imports を使います。
Viteで@/が解決できないとき
vite.config を置かずに開発サーバー(npx vite)を起動し、/src/main.ts を取得すると HTTP 500 になり、ターミナルには次のエラーが出ました。
[vite] Internal server error: Failed to resolve import "@/lib/greet" from "src/main.ts". Does the file exist?
一方、同じ状態の npx vite build は、Vite 8.3.2 では paths を読んで通りました(tsconfig.json から paths を消すと Rolldown failed to resolve import "@/lib/greet" で止まります)。ビルドは通るのに開発サーバーでは見つからない、という食い違いが起こるので、設定を明示して両方をそろえます。
方法1:resolve.tsconfigPathsを有効にする
Vite の公式ドキュメント(Shared Options)には、tsconfig.json の paths を解決に使う resolve.tsconfigPaths があり、既定値は false と書かれています。これを true にします。
// vite.config.ts
import { defineConfig } from "vite";
export default defineConfig({
resolve: {
tsconfigPaths: true,
},
});
開発サーバーが返す /src/main.ts は HTTP 200 になり、import が実際のパスに書き換わっていました。
import { greet } from "/src/lib/greet.ts";
エイリアスの定義が tsconfig.json の1か所で済むので、まずこの方法をおすすめします。同じドキュメントには、paths が適用されるのは tsconfig.json の files や include に含まれるファイルだけ、という注意も書かれています。
方法2:resolve.aliasに同じ対応を書く
// vite.config.ts
import { fileURLToPath } from "node:url";
import { defineConfig } from "vite";
export default defineConfig({
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
tsconfig.json と vite.config の2か所に同じ対応を書く方法です。公式ドキュメントのとおり、ファイルの場所を指すときは絶対パスにします。
方法3:vite-tsconfig-pathsプラグインを使う
// vite.config.ts
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [tsconfigPaths()],
});
npm i -D vite-tsconfig-paths で入れます(確認した版は 6.1.1)。
3つの方法の結果
| vite.config の内容 | 開発サーバー | vite build | Vitest |
|---|---|---|---|
| なし | HTTP 500(Failed to resolve import) | 通る | 止まる(Cannot find package) |
resolve.tsconfigPaths: true |
HTTP 200 | 通る | 1 passed |
resolve.alias |
HTTP 200 | 通る | 1 passed |
| vite-tsconfig-paths | HTTP 200 | 通る | 1 passed |
Vitestで@/が解決できないとき
// src/lib/greet.test.ts
import { expect, test } from "vitest";
import { greet } from "@/lib/greet";
test("greet", () => {
expect(greet("taro")).toBe("こんにちは、taroさん");
});
vite.config が無い状態で npx vitest run を実行した結果です(区切り線などを除いた抜粋)。
FAIL src/lib/greet.test.ts [ src/lib/greet.test.ts ]
Error: Cannot find package '@/lib/greet' imported from /home/taro/alias-demo/src/lib/greet.test.ts
❯ src/lib/greet.test.ts:2:1
1| import { expect, test } from "vitest";
2| import { greet } from "@/lib/greet";
| ^
Test Files 1 failed (1)
Tests no tests
Vitest は vite.config を読むので、前の節のどれか1つを vite.config.ts に書けば、テストでも同じエイリアスが使えます。 3つの方法すべてで次の結果になりました。
Test Files 1 passed (1)
Tests 1 passed (1)
Node.jsとtsxで@/が解決できないとき
Node.js 22.22.2 は .ts ファイルを直接実行できますが、tsconfig.json は読みません。
node src/main.ts
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@/lib' imported from /home/taro/alias-demo/src/main.ts
tsx は tsconfig.json の paths を読むので、同じファイルがそのまま動きます。
npx tsx src/main.ts
こんにちは、taroさん
tsconfig.json から paths を消して tsx で実行すると、Node.js と同じ ERR_MODULE_NOT_FOUND: Cannot find package '@/lib' になりました。paths を別のファイルに書いている場合は、npx tsx --tsconfig tsconfig.build.json src/main.ts のように読むファイルを指定できます。
package.jsonのimportsで設定を1か所にまとめる
Node.js には、package.json の imports に「# で始まる名前」と実際の場所の対応を書く仕組みがあります。TypeScript・Vite・Vitest もこの imports を読むため、paths も vite.config も書かずに、1か所の設定で全部のツールがそろいます。
#src/の形(Node.js 22でも動く)
{
"name": "imports-demo",
"private": true,
"type": "module",
"imports": {
"#src/*": "./src/*"
}
}
{
"compilerOptions": {
"target": "es2023",
"module": "nodenext",
"allowImportingTsExtensions": true,
"noEmit": true
},
"include": ["src"]
}
// src/main.ts
import { greet } from "#src/lib/greet.ts";
console.log(greet("taro"));
Node.js が .ts を直接読むため、import には拡張子 .ts まで書き、tsconfig.json には allowImportingTsExtensions を足しています。vite.config は置いていません。この状態での結果です。
| ツール | 結果 |
|---|---|
npx tsc(7.0.2) |
エラー0件 |
npx tsc(5.9.3) |
エラー0件 |
node src/main.ts(22.22.2) |
こんにちは、taroさん |
npx tsx src/main.ts |
こんにちは、taroさん |
| Vite 開発サーバー | HTTP 200(import { greet } from "/src/lib/greet.ts"; に書き換え) |
npx vite build |
通る |
npx vitest run |
1 passed |
#/の形はTypeScript 6.0以降とNode.js 24で使える
@/ に近い見た目の "#/*": "./src/*" も試しました。import は "#/lib/greet.ts" と書きます。
| ツール | 結果 |
|---|---|
| tsc 7.0.2 / 6.0.3 | エラー0件 |
| tsc 5.9.3 | error TS2307: Cannot find module '#/lib/greet.ts' or its corresponding type declarations. |
| Node.js 22.22.2 | ERR_INVALID_MODULE_SPECIFIER(下記) |
| Node.js 24.21.0 | こんにちは、taroさん |
| tsx(Node.js 22.22.2 上) | Node.js と同じ ERR_INVALID_MODULE_SPECIFIER |
| tsx(Node.js 24.21.0 上) | こんにちは、taroさん |
Vite 開発サーバー・vite build・Vitest |
すべて通る |
TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module "#/lib/greet.ts" is not a valid internal imports specifier name imported from /home/taro/imports-demo/src/main.ts
Node.js 22 でも動かす必要があるなら #src/*、Node.js 24 以降と TypeScript 6.0 以降にそろっているなら #/*、という選び方になります。
tscで出力したJSを実行する場合
tsc でビルドした dist を Node.js で実行する構成では、imports の対応先を出力先に向けます。
{
"name": "imports-build-demo",
"private": true,
"type": "module",
"imports": {
"#src/*": "./dist/*"
}
}
{
"compilerOptions": {
"target": "es2023",
"module": "nodenext",
"rootDir": "src",
"outDir": "dist"
},
"include": ["src"]
}
// src/main.ts
import { greet } from "#src/lib/greet.js";
console.log(greet("taro"));
npx tsc はエラー0件で、rootDir と outDir の対応から dist/lib/greet.js の元が src/lib/greet.ts だと判断して型チェックします。出力された dist/main.js の import は "#src/lib/greet.js" のままで、node dist/main.js は こんにちは、taroさん と表示しました。
pathsとimportsのどちらを選ぶか
| 状況 | おすすめ | 理由 |
|---|---|---|
| Vite で作るフロントエンド | paths + resolve.tsconfigPaths: true |
@/ の見た目のまま、定義は tsconfig.json の1か所 |
| Node.js で .ts を直接実行する、または tsc の出力を実行する | package.json の imports |
Node.js 自身が解決するので追加のツールが要らない |
| tsx で実行するスクリプト | paths のままでよい |
tsx が tsconfig.json を読む |
| ツールが多く、設定をそろえるのが手間 | imports(#src/*) |
今回試した5つのツールすべてが追加設定なしで解決した |
既存コードに @/ の import が大量にある |
paths を残し、ツールごとの設定を足す |
import の書き換えが要らない |
解決できないときの切り分け手順
- エラーを出しているのがどのツールかを見る。
error TS2307なら tsc、Failed to resolve importなら Vite、ERR_MODULE_NOT_FOUNDなら Node.js です。 - tsc のエラーなら
npx tsc --showConfigで paths が入っているかを見て、--traceResolutionで探した場所を確かめます。 - TS5102 や TS5090 なら
baseUrlを消して"./src/*"と書き直します。 - tsc は通るのに Vite や Vitest で止まるなら vite.config に
resolve.tsconfigPaths: trueを足します。 - tsc は通るのに node で止まるなら
importsに切り替えるか、tsx で実行します。 #/の import が Node.js で止まるならnode -vを確かめ、22 系なら#src/*の形に変えます。
よくある質問
Q. baseUrl を消すと、paths は何を基準に解決されますか?
A. その paths が書かれている tsconfig.json のあるフォルダです。extends で継承している場合も、paths を書いた側のファイルの場所が基準になります。--traceResolution の「candidate module location」で実際のパスを確かめられます。
Q. tsc の型チェックは通るのに、実行すると見つかりません。 A. paths は tsc に場所を教える設定で、実行時は Vite や Node.js がそれぞれの方法で解決します。冒頭の「ツール別・必要な設定」の表から、実行しているツールの行を確かめてください。
Q. vite build は通るのに、開発サーバーだけエラーになります。
A. Vite 8.3.2 で、vite.config が無い状態だとこの組み合わせになりました。resolve.tsconfigPaths: true(または resolve.alias)を書くと、開発サーバー・ビルド・Vitest の3つがそろいます。
Q. @/ を package.json の imports に書けますか?
A. "@/*": "./src/*" を imports に書いて試したところ、Node.js 22.22.2 と 24.21.0 のどちらでも ERR_MODULE_NOT_FOUND: Cannot find package '@/lib' のままでした。imports で使えるのは # で始まる名前です。@/ に近い形にしたい場合は #/*(TypeScript 6.0 以降と Node.js 24.21.0 で動作)か、#src/* を使います。
まとめ
- TypeScript 7.0 では
baseUrlを書かない:"@/*": ["./src/*"]と、対応先を./から書く。5.9.3・6.0.3・7.0.2 のすべてで通る。 - 6.0 は TS5101、7.0 は TS5102 と TS5090:
ignoreDeprecationsで抑えられるのは 6.0 の間だけ。 - paths は tsc に場所を教えるだけ:tsc は import を書き換えないので、出力した JS を node で実行すると ERR_MODULE_NOT_FOUND になる。
- Vite と Vitest は
resolve.tsconfigPaths: true:設定なしでは開発サーバーと Vitest が止まり、ビルドだけ通った。 - tsx は tsconfig.json の paths を読む:Node.js の直接実行は読まない。
- 設定を1か所にしたいなら package.json の
imports:#src/*は試した全ツールで追加設定なしに動いた。#/*は TypeScript 6.0 以降と Node.js 24 で使える。 - 切り分けはエラーを出したツールから:TS2307 は
--showConfigと--traceResolutionで原因を追える。
参考・出典
- Announcing TypeScript 6.0(baseUrl の扱いと移行方法) https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/
- Vite 公式ドキュメント Shared Options(resolve.alias / resolve.tsconfigPaths) https://vite.dev/config/shared-options
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「TypeScript pathsエイリアス設定|@/が解決できない原因と直し方」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。