Claude Media
Claude CodeでNuxtアプリを開発する — nuxt typecheckで型を検証する手順

Claude CodeでNuxtアプリを開発する — nuxt typecheckで型を検証する手順

Claude CodeでNuxt 3/4アプリを開発するときの、auto-importを踏まえたCLAUDE.mdの書き方と、nuxt typecheckとStop hookで型エラーを潰す検証ループを解説します。

Claude CodeでNuxtアプリを作るときの要は、nuxt typecheck を「終わったかどうか」の判定に据えることです。Nuxtは ref やコンポーザブルを自動でインポートするため、Claudeが書いたコードが動きそうに見えても、型の整合性は実行するまで分かりません。この記事では、Nuxtの規約をCLAUDE.mdに落とす方法と、型検査・開発サーバー起動確認・Stop hookを組み合わせた検証ループを説明します。

Nuxtで型検査を自動化する理由

Nuxtはコンポーネント、コンポーザブル、Vue APIを明示的なimportなしで使える仕組みを持ちます。app/composables/ や app/utils/ に置いた関数も自動でインポートされます。

書き手のClaudeにとっては、これが罠になります。importが無いので、存在しない関数を呼んでも見た目では気づけません。

しかもNuxtは既定で型を検査しません。nuxt dev や nuxt build を実行しても、パフォーマンスのため型チェックは走りません。

つまり、Claudeに「動くか確認して」と頼むだけでは、型エラーを見逃したまま完了報告が返ってきます。型を明示的に検査するコマンドを、検証手順として固定する必要があります。

nuxt typecheckの役割と前提

nuxt typecheck は vue-tsc またはGolarでアプリ全体の型を検査するコマンドです。どちらも未インストールの場合は、対話端末ならインストールを促され、非対話の端末ではインストール手順が表示されます。

Claudeに走らせる前に、開発依存として入れておくのが確実です。

npm install --save-dev vue-tsc typescript
npx nuxt typecheck

主なオプションは次のとおりです。

オプション役割
ROOTDIR役割作業ディレクトリ(既定は .)
--cwd=<directory>役割作業ディレクトリ。ROOTDIRより優先される
--dotenv役割読み込む .env のパス
-e, --extends=<layer-name>役割Nuxtレイヤーを継承する
--checker役割型チェッカーの指定(vue-tsc か golar)

注意点が1つあります。このコマンドは process.env.NODE_ENV を production に設定します。開発用の値で検査したい場合は、.env かコマンドライン引数で NODE_ENV を上書きします。

型検査を開発・ビルド時に常時有効にする方法もあります。nuxt.config.ts で typescript.typeCheck を true にします。

export default defineNuxtConfig({
  typescript: {
    typeCheck: true,
  },
})

Claudeへの指示としては、独立したコマンドの nuxt typecheck を明示するほうが、成功と失敗を切り分けやすくなります。

生成される型が古いと誤検知する

自動インポートの型は .nuxt/imports.d.ts に生成されます。生成のタイミングは nuxt prepare、nuxt dev、nuxt build のいずれかを実行したときです。

コンポーザブルを新しく作った直後に、開発サーバーを起動せず型検査すると Cannot find name 'useBar'. のようなエラーが出ます。Claudeが新しいファイルを足した後に、これを本物のバグと誤解して修正を始めるのを防ぐため、検証手順は nuxt prepare から始めます。

Nuxt 3と4でディレクトリ構成が違う

Nuxt 4では、srcDir の既定値が app/ になります。components/、composables/、pages/、app.vue などは app/ の下に置きます。一方 server/、public/、shared/、modules/、nuxt.config.ts はプロジェクトルートに残ります。

Nuxt 3の構成(ルート直下に components/ などを置く形)も互換性のために自動検出されます。移行は必須ではありません。

CLAUDE.mdに書くべきなのは、いまのプロジェクトがどちらの構成かです。構成を書かないと、Claudeが app/composables/ と composables/ のどちらに新規ファイルを置くか迷い、自動インポートの対象外に置いてしまうことがあります。

CLAUDE.mdに書くNuxtの規約

Claude Codeは、CLAUDE.mdをセッション開始時にコンテキストへ読み込みます。公式は、1ファイルあたり200行未満を目安とし、具体的で検証できる書き方を勧めています。「テストを実行する」ではなく「npm test を実行する」のように書きます。

Nuxt 4プロジェクトでの例を示します。これはドキュメントの記載例ではなく、上の指針とNuxtの仕様から組み立てた一例です。

# Nuxtプロジェクトの規約
 
## 構成
- Nuxt 4。アプリのコードは `app/` 配下、APIは `server/` 配下
- コンポーネントは `app/components/`、コンポーザブルは
  `app/composables/` の直下に置く(サブディレクトリは自動インポート対象外)
- `ref` `computed` `useFetch` などは自動インポートされる。
  手でimportを書かない
- `server/` から `app/` のコードをimportしない(逆も同様)
 
## 検証コマンド
- 型生成: `npx nuxt prepare`
- 型検査: `npx nuxt typecheck`(完了前に必ず実行し、エラー0にする)
- 起動確認: `npx nuxt dev`(http://localhost:3000)

この例には、Nuxt特有の3つの規約が入っています。

  • コンポーザブルは直下に置く: Nuxtが走査するのは app/composables/ の最上位のファイルだけで、ネストしたディレクトリは対象外です。ネストさせたい場合は index.ts から再エクスポートします
  • 手でimportを書かない: 明示的に書きたい場合は #imports エイリアスを使います
  • appとserverを混ぜない: サーバールートにVueのアプリコードをimportしない、とNuxtのドキュメントが明記しています

領域ごとに規約を分ける

Nuxtではフロントとサーバーで守るべき規約が違います。CLAUDE.mdを膨らませると読み込みコストが増えるので、.claude/rules/ にパス指定付きで分けます。paths を指定した規約は、該当ファイルをClaudeが読んだときに読み込まれます。

---
paths:
  - "server/**/*.ts"
---
 
# サーバーAPIの規約
 
- ハンドラーは `defineEventHandler()` で書く
- `server/api/` のファイルは `/api` が付いたルートになる
- `app/` のコードをimportしない

server/api/hello.ts が /api/hello に、server/routes/hello.ts が /hello に対応するのがNuxtの規約です。この対応をルールに書いておくと、Claudeが新しいエンドポイントを作るときにファイルの置き場所を間違えにくくなります。

型検査を反復させる検証ループ

規約を書いても、Claudeが手順を飛ばす可能性は残ります。そこで、次の順序を依頼文かCLAUDE.mdで固定します。

  1. npx nuxt prepare で型を再生成する
  2. npx nuxt typecheck を実行し、エラーがあれば直す
  3. エラーが0になるまで2を繰り返す
  4. npx nuxt dev で起動し、http://localhost:3000 で画面を確認する

依頼文の例は次のとおりです。

ユーザー一覧ページを app/pages/users.vue に追加して。
完了前に nuxt prepare → nuxt typecheck を実行し、
エラーが残っていれば修正して再実行して。
最後に nuxt dev を起動して、/users が表示できるところまで確認して。

型エラーの出力は、Claudeがそのまま読んで修正できます。人間がエラーを転記する必要はありません。ここが型検査コマンドを検証に使う利点です。

開発サーバーの起動確認は、型検査では拾えない問題に効きます。たとえばNuxtのコンポーザブルは、プラグイン・ルートミドルウェア・Vueのsetup関数の外で呼ぶと Nuxt instance is unavailable というエラーになります。この種の文脈依存のエラーは、実行して初めて表面化します。

サーバーAPIとページを型でつなぐ例

型検査の効果が出やすいのは、server/ と app/ の境目です。ドキュメントのサンプルに近い形で、次のようなAPIとページの組を考えます。

// server/api/hello.ts
export default defineEventHandler((event) => {
  return {
    hello: 'world',
  }
})
<!-- app/pages/index.vue -->
<script setup lang="ts">
const { data } = await useFetch('/api/hello')
</script>
 
<template>
  <pre>{{ data }}</pre>
</template>

Nuxtが生成するtsconfigには、自動インポートの参照に加えてAPIルートの型も含まれます。そのため、APIの戻り値を変えたのにページ側が古い形を前提にしている、といった不整合を型検査が拾えます。Claudeにサーバー側だけを修正させた場合でも、nuxt typecheck を通せばページ側の追従漏れが出力に現れます。

依頼文には「APIとページを両方直して、typecheckが通るまで」と書いておくと、片側だけの修正で終わりにくくなります。

CIとpostinstallにも同じ検査を置く

ローカルでの検証手順は、CIでも共通化できます。nuxt prepare は、CI環境や package.json の postinstall で役立つコマンドです。

{
  "scripts": {
    "postinstall": "nuxt prepare",
    "typecheck": "nuxt typecheck"
  }
}

こうしておくと、依存関係を入れ直すたびに型が再生成されます。Claudeが npm install した直後でも、型定義が古い状態で検査が始まることはありません。CLAUDE.mdの検証コマンドも npm run typecheck の1行に短縮できます。

CIとローカルで同じコマンドを使う利点は、Claudeが直したエラーと、CIが落ちるエラーが一致することです。手元では通るのにCIで落ちる、という往復を減らせます。

Stop hookで型エラーを残したまま終わらせない

依頼文でループを指示しても、Claudeが省略する場合があります。Claude Codeのhookなら、手順をLLMの判断に任せずに強制できます。

Stopイベントは、Claudeが応答を終えるときに発火します。このhookが終了コード2を返すと、Claudeの停止が阻止されて会話が続きます。型エラーがあるあいだは終われない構成にできます。

.claude/settings.json に次を追加します。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/nuxt-typecheck.sh"
          }
        ]
      }
    ]
  }
}

スクリプトの例です。hookのガイドが示す stop_hook_active の確認を入れています。

#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0
fi
 
if ! OUT=$(npx nuxt typecheck 2>&1); then
  echo "$OUT" | tail -n 40 >&2
  exit 2
fi
exit 0

stop_hook_active の確認が必要な理由は、Claude Codeが「停止を連続8回ブロックした時点で、次のブロックを無視してターンを終える」上限を持つからです。この確認がないと、直せない型エラーが残った場合に、8回まで空回りします。上限は CLAUDE_CODE_STOP_HOOK_BLOCK_CAP で変更できます。

hookは、ファイルの編集ごとではなくStopに置いています。nuxt typecheck はアプリ全体を検査するコマンドなので、編集のたびに走らせるより、応答の締めに1回走らせる形が扱いやすいためです。個別ファイルの整形やLintを編集直後に回す設計は、PostToolUse hookのLint自動修正を参考にできます。テストの自動実行との使い分けはClaude Code hooksでテストを自動実行するで扱っています。

よくあるつまずき

症状原因の候補対処
Cannot find name 'useXxx'.原因の候補新規コンポーザブルの型がまだ生成されていない対処nuxt prepare を先に実行する
コンポーザブルが自動インポートされない原因の候補ネストしたディレクトリに置いた対処直下に置くか、index.ts で再エクスポートする
Nuxt instance is unavailable原因の候補Nuxtのコンテキスト外でコンポーザブルを呼んだ対処プラグイン・ミドルウェア・setup関数の内側に移す
typecheckがインストールを求めて止まる原因の候補vue-tsc もGolarも未導入対処devDependencyに vue-tsc と typescript を入れる
Stop hookが延々と再実行される原因の候補stop_hook_active を見ていない対処スクリプトの先頭で確認して exit 0 する

tsconfig.json を手で直したくなる場面もありますが、Nuxtは推奨していません。.nuxt/ 配下の設定はNuxtとモジュールが生成・拡張するためで、変更は nuxt.config.ts 経由で行います。この点もCLAUDE.mdに1行書いておくと、Claudeが型エラーの回避策としてtsconfigを書き換える事態を避けられます。

Nuxtのプロジェクト構成が変わるときの扱い

Nuxt 3から4へ移行するプロジェクトでは、CLAUDE.mdの「構成」の節が真っ先に古くなります。Nuxt 4の移行手順は、components/、composables/、utils/、app.vue などを app/ の下へ移し、nuxt.config.ts、server/、public/、shared/、modules/ はルートに残す、というものです。

移行作業をClaudeに任せるなら、次の順序が安全です。

  1. 移行前に nuxt typecheck を実行し、エラー数を基準として控える
  2. ディレクトリを移し、CLAUDE.mdの「構成」の節を書き換える
  3. nuxt prepare で型を再生成する
  4. nuxt typecheck を再実行し、基準より増えていないことを確認する

移行前後で型エラーの数を比べる手順は、ドキュメントに書かれたものではなく、この記事の提案です。ただし、ファイルの移動だけで意味が変わる作業には、機械的に比較できる基準があると判断しやすくなります。

srcDir をすでにカスタマイズしているプロジェクトは注意が必要です。modules/、public/、shared/、server/ は srcDir ではなく rootDir から解決されます。CLAUDE.mdには、カスタムしている srcDir の値もそのまま書いておきます。

他のフレームワークとの違い

Nuxtの検証手順が独特なのは、型の生成が事前ステップとして必要な点です。規約の強いフレームワークでは、コマンドの実行順序がそのまま検証の質を左右します。同じ発想のLaravel向けの設計はClaude CodeでLaravelアプリを開発するに、PythonのMVT構成はClaude CodeでDjangoアプリを開発するにあります。

Nuxtでは、CLAUDE.mdに構成・自動インポートの範囲・検証コマンドの3点を書き、実行はhookで担保する。この分担にしておくと、依頼文が短くても、Claudeが型エラーを残したまま完了を宣言する事態を減らせます。

この記事を共有:XはてブLinkedIn