要点まとめ
- Node側(設定/ビルド/スクリプト)とブラウザ側(アプリ本体)の型世界は分離するのが基本
- まずは“緩め”で開発速度重視 → バグの芽が見えたら“固め”へ段階的に引き上げる
- Vitest を使うなら
types
にvitest
、Vite の環境変数補完にvite/client
を入れる - CI では
vue-tsc --noEmit
を併用して SFC の型を厳格にチェックする
なぜ分ける?(Node / App)
- Node側:
vite.config.ts
,vitest.config.ts
,playwright.config.ts
など。process
,fs
など Node固有の型を使う - App側:
src/**/*.ts
,src/**/*.vue
。window
,document
など DOMの型や.vue
SFC を扱う
同じ設定で混在させると、Nodeの型とDOMの型が衝突しやすいため、プロジェクト参照で分離するのがベストプラクティスです。
ディレクトリ前提
.
├─ tsconfig.json
├─ tsconfig.node.json
├─ tsconfig.app.json
├─ env.d.ts
├─ vite.config.ts
├─ vitest.config.ts
└─ src/
├─ main.ts
├─ App.vue
└─ ...
1) “緩め”の tsconfig(導入しやすい・スピード優先)
tsconfig.json(ルート:参照元)
{
"files": [],
"references": [
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.app.json" }
]
}
tsconfig.node.json(Node / ツール)
{
"extends": "@tsconfig/node22/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*",
"eslint.config.*"
],
"compilerOptions": {
"noEmit": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}
tsconfig.app.json(App / ブラウザ)
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"skipLibCheck": true,
"isolatedModules": true,
"useDefineForClassFields": true,
"paths": {
"@/*": ["./src/*"]
},
"types": ["vite/client", "vitest"]
}
}
env.d.ts(SFC & Vite補完)
/// <reference types="vite/client" />
ポイント(緩め)
-
skipLibCheck: true
で依存型チェックをスキップし、開発とCIを高速化 -
isolatedModules: true
は Vite/esbuild と相性が良い -
types
にvitest
,vite/client
を追加して補完を効かせる
2) “固め”の tsconfig(品質優先・堅牢)
tsconfig.json(ルート:参照元)
{
"files": [],
"references": [
{ "path": "./tsconfig.node.json" },
{ "path": "./tsconfig.app.json" }
]
}
tsconfig.node.json(Node / ツール)
{
"extends": "@tsconfig/node22/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"playwright.config.*",
"eslint.config.*"
],
"compilerOptions": {
"noEmit": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"],
"strict": true,
"verbatimModuleSyntax": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true
}
}
tsconfig.app.json(App / ブラウザ)
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"useDefineForClassFields": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"paths": {
"@/*": ["./src/*"]
},
"types": ["vite/client", "vitest"]
}
}
env.d.ts
/// <reference types="vite/client" />
/// <reference types="vitest" />
ポイント(固め)
-
strict: true
を基本とし、未定義アクセスやオプショナルを厳密に扱う -
noUncheckedIndexedAccess
,exactOptionalPropertyTypes
でバグを型で検出 -
verbatimModuleSyntax
,noImplicitOverride
などでコードの明示性を強化
Vitest 連携の補足
-
types
にvitest
を入れるとdescribe/it/expect
が補完される -
vitest.config.ts
でenvironment: 'jsdom'
を指定すれば DOM API を使える - グローバルAPIを使うなら
globals: true
を有効化する
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./src/test/setup.ts']
}
})
実務Tips
- Vue SFC の型チェックは
vue-tsc
を利用する -
tsconfig.app.json
のpaths
とvite.config.ts
のresolve.alias
は一致させる - “固め化”は段階的に進めると移行しやすい
おすすめの順番:
strict: true
-
isolatedModules: true
,useDefineForClassFields: true
verbatimModuleSyntax: true
noUncheckedIndexedAccess: true
exactOptionalPropertyTypes: true
-
noImplicitOverride: true
,noFallthroughCasesInSwitch: true
まとめ
- Vue + Vite + Vitest では Node用とApp用の tsconfig を分けて管理するのが基本
- 小規模・プロトタイプでは“緩め”でスピード重視
- 中〜大規模・長期運用では“固め”へ段階的に移行するのがベスト
- CIに
vue-tsc
を導入し、型で検知できるバグは早めに潰すのが実務的