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 の書き換えが要らない

解決できないときの切り分け手順

  1. エラーを出しているのがどのツールかを見る。 error TS2307 なら tsc、Failed to resolve import なら Vite、ERR_MODULE_NOT_FOUND なら Node.js です。
  2. tsc のエラーなら npx tsc --showConfig で paths が入っているかを見て、--traceResolution で探した場所を確かめます。
  3. TS5102 や TS5090 なら baseUrl を消して "./src/*" と書き直します。
  4. tsc は通るのに Vite や Vitest で止まるなら vite.config に resolve.tsconfigPaths: true を足します。
  5. tsc は通るのに node で止まるなら imports に切り替えるか、tsx で実行します。
  6. #/ の 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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?