【Nuxt 3 vs Nuxt 4】TypeScriptのサポート強化について
はじめに
2025年7月16日にNuxt 4がリリースされました。
前回はNuxt 4でパフォーマンスが改善されたuseAsyncData
について、紹介しました。
今回はNuxt 4でサポートが強化されたTypeScriptについて、フォーカスしていきたいと思います。
公式ドキュメントや関連記事を調べて、Nuxt 3とNuxt 4のTypeScriptの違いを実際に検証してみました。
Nuxt 4のTypeScriptのサポート強化
Nuxt公式には以下のように書かれています。
Better TypeScript experience
Nuxt now creates separate TypeScript projects for your app code, server code, shared/ folder, and builder code. This should mean better autocompletion, more accurate type inference and fewer confusing errors when you're working in different contexts.TypeScriptエクスペリエンスの向上
Nuxt は、アプリコード、サーバーコード、shared/フォルダ、ビルダーコードごとに個別の TypeScript プロジェクトを作成するようになりました。これにより、自動補完機能が向上し、型推論の精度が向上し、異なるコンテキストで作業する際に発生するエラーが軽減されます。
https://nuxt.com/blog/v4#better-typescript-experience
また、関連記事は以下のように補足が書かれました。
Navigating the new TypeScript experience
Nuxt 4 introduces a revamped TypeScript setup that eliminates longstanding issues with type safety and context confusion.Eliminating ‘type-bleed’
In Nuxt 3, a single monolithic tsconfig.json handled types across the entire project. This often caused “type-bleed,” where types from one environment (like server-side Node.js utilities) leaked into another (like client-side Vue components). The result was hidden bugs or confusing runtime errors.Nuxt 4 fixes this by creating separate, virtual TypeScript projects for each context: app/, server/, and shared/. This strict separation ensures precise type inference. Server-only utilities are correctly flagged as unavailable in client-side code — and vice versa.
Simplifying tsconfig.json
For most projects, upgrading means removing complexity. Nuxt now handles path mapping and context separation internally.Surfacing hidden type errors
The stricter typing may reveal errors that went unnoticed in Nuxt 3. While this may feel like extra work during migration, it greatly improves long-term stability.新しい TypeScript エクスペリエンスの操作
Nuxt 4 では改良された TypeScript セットアップが導入され、型の安全性とコンテキストの混乱に関する長年の問題が解消されています。「タイプブリード」の排除
Nuxt 3では、プロジェクト全体の型を単一のモノリシックな構造で処理していました。そのため、ある環境(サーバーサイドのNode.jsユーティリティなど)の型が別の環境(クライアントサイドのVueコンポーネントなど)に漏れてしまう「型漏れ」が頻繁に発生していました。その結果、隠れたバグや分かりにくいランタイムエラーが発生していました。Nuxt 4では、コンテキストごとに個別の仮想TypeScriptプロジェクトを作成することでこの問題を修正しました。この厳密な分離により、正確な型推論が保証されます。サーバー側のみのユーティリティは、クライアント側のコードでは使用不可として適切にフラグ付けされ、その逆も同様です。
簡素化tsconfig.json
ほとんどのプロジェクトにとって、アップグレードとは複雑さの排除を意味します。Nuxt はパスマッピングとコンテキスト分離を内部で処理するようになりました。隠れた型エラーの発見
より厳密な型指定により、Nuxt 3 では気付かなかったエラーが明らかになる可能性があります。これは移行中に余分な作業のように感じるかもしれませんが、長期的な安定性が大幅に向上します。
https://blog.logrocket.com/nuxt-4-0-whats-new-what-to-expect/#navigating-new-typescript-experience
下記、Nuxt 3とどう違うか、実際に検証していきたいと思います。
検証プロジェクトの準備
検証 1. Type-bleed(型の漏れ)の検証
この検証では、サーバー専用の関数をクライアントコンポーネントでインポートしたときの挙動の違いを確認しました。
① プロジェクトの作成
# Nuxt 3 プロジェクト
npx nuxi@3 init nuxt3-ts-test
cd nuxt3-ts-test
npm install nuxt@3.9.2
# Nuxt 4 プロジェクト
npx nuxi@4 init nuxt4-ts-test
cd nuxt4-ts-test
npm install
② TypeScript の有効化
両方のプロジェクトで以下をインストールします。
npm install --save-dev typescript vue-tsc
③ サーバーユーティリティの作成
server/utils/serverLogger.ts
を作成します。(両プロジェクトで同じ内容)
// server/utils/serverLogger.ts
import fs from 'node:fs' // Nodeのファイル操作モジュール
import path from 'node:path' // Nodeのパス操作モジュール
/**
* サーバー専用のロガー - Node.js の fs モジュールを使用
* クライアント側では動作しません
*/
export function logToFile(message: string) {
const logPath = path.join(process.cwd(), 'server.log') // 現在の作業ディレクトリにログを作成
fs.appendFileSync(logPath, `${new Date().toISOString()}: ${message}\n`)
return `Logged: ${message}`
}
export function getServerInfo() {
return {
platform: process.platform, // OSプラットフォーム
nodeVersion: process.version, // Nodeのバージョン
memory: process.memoryUsage() // プロセスのメモリ使用量
}
}
④ 問題のあるクライアントコンポーネントの作成
components/BadComponent.vue
を作成します。(両プロジェクトで同じ内容)
<!-- components/BadComponent.vue -->
<template>
<div class="bad-component">
<h2>Type-bleed テスト</h2>
<button @click="handleClick">サーバー関数を呼び出す</button>
<button @click="showServerInfo">サーバー情報を表示</button>
<p v-if="result">{{ result }}</p>
</div>
</template>
<script setup lang="ts">
// ❌ これはクライアントコンポーネントでサーバーユーティリティをインポート
// Nuxt 3: 型エラーが出ない(type-bleed)
// Nuxt 4: 明確に型エラーが出る
import { logToFile, getServerInfo } from '~/server/utils/serverLogger'
const result = ref<string>('')
function handleClick() {
// ブラウザでは fs モジュールが存在しないため実行時エラー
try {
const message = logToFile('Button clicked from client')
result.value = message
} catch (error) {
result.value = `Error: ${error}`
}
}
function showServerInfo() {
try {
const info = getServerInfo()
result.value = JSON.stringify(info, null, 2)
} catch (error) {
result.value = `Error: ${error}`
}
}
</script>
<style scoped>
.bad-component {
padding: 20px;
border: 2px solid red;
margin: 20px;
}
</style>
⑤ ページでコンポーネントを使用
pages/index.vue
を作成します。
<!-- pages/index.vue -->
<template>
<div>
<h1>Nuxt TypeScript Type-bleed テスト</h1>
<BadComponent />
</div>
</template>
⑥ 型チェックの実行
両プロジェクトで以下のコマンドを実行します。
# 開発サーバーを起動
npm run dev
# 型チェックを実行
npx nuxt typecheck
検証結果
Nuxt 3
Nuxt 4
Nuxt 3での結果として、型チェック時にエラーが出ませんでした。一方、Nuxt 4では明確なエラーメッセージが表示されました。
検証 2. tsconfig.jsonの構造比較
① tsconfig.json の作成
Nuxt 3 プロジェクトの tsconfig.json
:
{
"extends": "./.nuxt/tsconfig.json",
"compilerOptions": {
"strict": true,
"baseUrl": ".",
"paths": {
"~/*": ["./*"],
"@/*": ["./*"],
"~~/*": ["./*"],
"@@/*": ["./*"],
"#app": ["./.nuxt/types/app.d.ts"]
},
"types": ["@nuxt/types"]
},
"include": [
"./components/**/*",
"./composables/**/*",
"./layouts/**/*",
"./pages/**/*",
"./plugins/**/*",
"./server/**/*",
"./app.vue"
]
}
Nuxt 4 プロジェクトの tsconfig.json
:
{
"compilerOptions": {
"strict": true
}
}
検証結果
両プロジェクトで npm run dev
を実行後、.nuxt
ディレクトリ内のファイルを確認します。
Nuxt 3で確認するファイルとして、.nuxt/tsconfig.json
があります。
Nuxt 4で確認するファイルには、以下のものがあります:
-
.nuxt/tsconfig.json
(後方互換性のため) -
.nuxt/tsconfig.app.json
(アプリケーションコード用) -
.nuxt/tsconfig.server.json
(サーバーコード用) -
.nuxt/tsconfig.shared.json
(共有コード用) -
.nuxt/tsconfig.node.json
(nuxt.config 用)
検証 3: IDEの自動補完の違い
① コンポーネント内で自動補完をテスト
components/AutocompleteTest.vue
を作成し、<script setup>
内で以下を試します。
<script setup lang="ts">
// 型を入力してみて、IDE の候補を観察
// Nuxt 3: サーバー専用の型も候補に表示される
// Nuxt 4: コンテキストに応じた候補のみが表示される
import { } from '~/'
</script>
検証結果
VSCode でこのファイルを開き、インポート文を書きながら自動補完の候補を確認します。
Nuxt 3
Nuxt 4
Nuxt 4では、クライアントコンポーネント内ではサーバーユーティリティが候補に表示されないことを確認しました。
② サーバーファイル内で自動補完をテスト
server/api/autocomplete-test.ts
を作成し、同様に自動補完を確認します。
// server/api/autocomplete-test.ts
export default defineEventHandler((event) => {
// ここで node: モジュールの補完を確認
import('node:')
return { test: true }
})
検証結果
Nuxt 3
Nuxt 4
Nuxt 4では、サーバーコンテキスト内でNode.js組み込みモジュールの補完が機能することを確認しました。
あとがき
今回、Nuxt 3と Nuxt 4のTypeScriptを比べてみて、型エラーチェックやIDEの自動補完に大きな差があることを実感しました。
Nuxt 3ではサーバー専用の関数もクライアント側で使えるように見えてしまい、うっかり間違ってしまう可能性があります。
Nuxt 4では場所ごとに使える関数をちゃんと分けてくれるので、こうしたミスを事前に防げます。
ちょっとした検証でも、型の安全性が高まると開発がと安心で効率的になることが分かりました。Nuxt 4は設定もシンプルで補完も快適、TypeScriptを使う人には嬉しいアップデートです。
Discussion