【TypeScript × Vite】完全セットアップガイド|vite.config.ts・パスエイリアス・環境変数の型定義・Vitest連携まで徹底解説

【TypeScript × Vite】完全セットアップガイド|vite.config.ts・パスエイリアス・環境変数の型定義・Vitest連携まで徹底解説 TypeScript

Viteは「ヴィート」と読む、フランス語で「速い」を意味するフロントエンドビルドツールです。Vueの作者であるEvan Youが開発し、今やReact・Vue・Svelteを問わず広く使われています。

TypeScriptと組み合わせるとき、「vite.config.tsの書き方がわからない」「パスエイリアスの設定でハマった」「環境変数の型がstringしか付かない」といった疑問が出てきます。本記事ではVite + TypeScript環境を一から構築し、実務で必要な設定を体系的に解説します。

この記事でわかること

  • Viteが速い理由とTypeScriptとの相性
  • npm create vite@latestでプロジェクトを作成する手順
  • vite.config.tsの型定義とdefineConfig()の使い方
  • パスエイリアス(@/)の設定方法(tsconfig連携含む)
  • import.meta.envの型定義と環境変数の管理
  • 型チェックとビルドを分離する理由と設定方法
  • Vitestとの統合設定

Viteを初めて使う方から、すでに使っているが設定に自信がない方まで、この記事で実務レベルの環境が整います。

なお、TypeScriptのtsconfig.json全般についてはTypeScript tsconfig.json 完全ガイドもあわせてご覧ください。

スポンサーリンク

ViteとTypeScriptの相性が良い理由

Viteには「開発サーバーが速い」以外にも、TypeScriptと相性が良い特徴があります。

特徴 内容
TypeScript標準サポート 追加プラグイン不要でTSファイルをそのまま扱える。.tsファイルをesbuildでトランスパイル
vite.config.tsが書ける 設定ファイル自体をTypeScriptで書けるため、型補完・型検証が効く
ESモジュールネイティブ TypeScriptのESM設定(moduleResolution: "bundler")との相性が良い
環境変数の型定義 import.meta.envを拡張する仕組みがあり、型安全に環境変数を扱える
Vitest Viteと共通設定で動くテストランナー。Jestより型まわりの設定が少ない
Viteはトランスパイルのみ・型チェックはしない
Viteの開発サーバーはesbuildでTSをトランスパイルするだけで型チェックを行いません。型エラーがあっても開発中は気付きにくいため、CIやエディタでtsc --noEmitを別途実行する必要があります。これはViteの重要な特性なので、後述の「型チェックとビルドの分離」で詳しく解説します。

プロジェクトの作成

Viteはプロジェクト作成用のCLIを提供しています。対話形式でフレームワークと言語を選べます。

ターミナル
# npm
npm create vite@latest my-app

# yarn
yarn create vite my-app

# pnpm
pnpm create vite my-app

コマンドを実行すると対話形式でフレームワークを選択できます。

ターミナル(対話形式)
? Select a framework: › - Use arrow-keys.
    Vanilla
    Vue
  ❯ React
    Preact
    Lit
    Svelte
    Solid
    Qwik
    Angular
    Others

? Select a variant: › - Use arrow-keys.
    TypeScript
  ❯ TypeScript + SWC
    JavaScript
    JavaScript + SWC
TypeScriptとTypeScript + SWCの違い

  • TypeScript: esbuildでトランスパイル(デフォルト)
  • TypeScript + SWC: Rustで書かれたSWCでトランスパイル。Reactのfastリフレッシュが高速になる

新規プロジェクトではSWCを選ぶとHMR(Hot Module Replacement)がさらに速くなるためおすすめです。

ターミナル
cd my-app
npm install
npm run dev

デフォルトではhttp://localhost:5173で開発サーバーが起動します。

生成されるファイル構成

Reactテンプレートで生成されるファイル構成は以下の通りです。

ディレクトリ構成
my-app/
├── public/            # 静的ファイル(ビルド時にそのままコピー)
├── src/
│   ├── assets/
│   ├── App.tsx
│   ├── main.tsx       # エントリーポイント
│   └── vite-env.d.ts  # Vite環境変数の型定義
├── index.html         # エントリーHTML(publicではなくルートに置く)
├── package.json
├── tsconfig.json      # ルートのtsconfig(参照設定)
├── tsconfig.app.json  # アプリコードのtsconfig
├── tsconfig.node.json # vite.config.ts用のtsconfig
└── vite.config.ts
Vite v5からのtsconfig分割
Vite v5以降のテンプレートではtsconfig.jsonが3つに分割されています。tsconfig.jsonはルートのみでreferencesとしてtsconfig.app.jsontsconfig.node.jsonを参照します。tsconfig.app.jsonがブラウザ向けコード、tsconfig.node.jsonがVite設定ファイル向けです。これはTypeScriptのProject Referencesという機能を活用したものです。

生成されるtsconfig.jsonの解説

Vite + React + TypeScriptテンプレートで生成されるtsconfig.app.jsonを確認します。

tsconfig.app.json(生成されるデフォルト)
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* バンドラーモード */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "isolatedModules": true,
    "moduleDetection": "force",
    "noEmit": true,

    /* リント */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src"]
}
設定 意味
moduleResolution: "bundler" Vite(バンドラー)向けの解決方法。拡張子省略・index.ts省略が使える
allowImportingTsExtensions import "./foo.ts"のように拡張子付きでインポートできる
isolatedModules: true esbuildでの単一ファイルトランスパイルに対応。const enumなどの非対応構文を禁止
noEmit: true 型チェックのみ実行(JSファイルの生成はViteに任せる)
noUnusedLocals/Parameters 未使用の変数・引数をエラーにする

vite.config.tsの書き方

vite.config.tsはViteの設定ファイルです。defineConfig()でラップすることで型補完が効きます。

vite.config.ts(基本形)
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
});

defineConfig()にはコールバック関数を渡すこともできます。これによりmode(development/production)やcommand(serve/build)を参照できます。

vite.config.ts(コールバック形式)
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig(({ command, mode }) => {
  console.log("command:", command); // "serve" または "build"
  console.log("mode:", mode);       // "development" または "production"

  return {
    plugins: [react()],
    build: {
      sourcemap: mode === "development",
    },
  };
});

よく使う設定オプション

vite.config.ts(実務でよく使う設定)
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],

  // 開発サーバーの設定
  server: {
    port: 3000,           // ポート番号
    open: true,           // 起動時にブラウザを開く
    cors: true,           // CORSを有効化
    proxy: {
      // /api/* へのリクエストをバックエンドにプロキシ
      "/api": {
        target: "http://localhost:8080",
        changeOrigin: true,
      },
    },
  },

  // ビルドの設定
  build: {
    outDir: "dist",       // 出力ディレクトリ
    sourcemap: true,      // ソースマップ生成
    target: "es2020",     // ターゲット環境
    rollupOptions: {
      output: {
        // チャンクの分割設定
        manualChunks: {
          vendor: ["react", "react-dom"],
        },
      },
    },
  },

  // 依存関係の最適化
  optimizeDeps: {
    include: ["react", "react-dom"],
  },
});

パスエイリアスの設定

プロジェクトが大きくなると../../components/Buttonのような相対パスが煩雑になります。@/などのエイリアスを使うと@/components/Buttonとシンプルに書けます。

設定は2箇所に必要です。①Viteのモジュール解決②TypeScriptの型解決を両方設定しないと、実行時エラーまたは型エラーが発生します。

vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
      "@components": path.resolve(__dirname, "./src/components"),
      "@hooks": path.resolve(__dirname, "./src/hooks"),
      "@utils": path.resolve(__dirname, "./src/utils"),
      "@types": path.resolve(__dirname, "./src/types"),
    },
  },
});
tsconfig.app.json(pathsを追加)
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@components/*": ["./src/components/*"],
      "@hooks/*": ["./src/hooks/*"],
      "@utils/*": ["./src/utils/*"],
      "@types/*": ["./src/types/*"]
    }
  }
}
pathの型定義が必要
import path from "path"vite.config.ts内で使うには@types/nodeが必要です。npm install --save-dev @types/nodeでインストールしてください。またtsconfig.node.jsontypes"node"を追加してください。

vite-tsconfig-pathsプラグインを使う方法

vite.config.tsresolve.aliastsconfig.jsonpathsを二重管理するのが手間な場合、vite-tsconfig-pathsプラグインを使うとtsconfig.jsonの設定を自動でViteに適用できます。

ターミナル
npm install --save-dev vite-tsconfig-paths
vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
  plugins: [
    react(),
    tsconfigPaths(), // tsconfig.json の paths を自動でViteに適用
  ],
});

このプラグインを使うとtsconfig.app.jsonpathsを設定するだけで済み、resolve.aliasが不要になります。

使用例
// Before: 相対パス(深いネストで読みにくい)
import { Button } from "../../components/ui/Button";
import { useAuth } from "../../../hooks/useAuth";

// After: パスエイリアス(すっきり読める)
import { Button } from "@/components/ui/Button";
import { useAuth } from "@/hooks/useAuth";

環境変数の型定義

Viteではimport.meta.envを通じて環境変数にアクセスします。デフォルトではstring型のみで型補完が利きません。型定義ファイルを作成することで型安全に使えます。

基本の仕組み

Viteは.envファイルの変数を読み込みますが、VITE_プレフィックスが付いた変数のみクライアントコードに公開されます。

.env
# VITE_ プレフィックスつき → クライアントコードに公開される
VITE_API_BASE_URL=https://api.example.com
VITE_APP_TITLE=MyApp
VITE_FEATURE_FLAG_NEW_UI=true

# VITE_ なし → サーバーサイド専用(クライアントからアクセス不可)
DATABASE_URL=postgresql://...
SECRET_KEY=super-secret
.env.development
VITE_API_BASE_URL=http://localhost:8080
VITE_ENABLE_DEVTOOLS=true
.env.production
VITE_API_BASE_URL=https://api.example.com
VITE_ENABLE_DEVTOOLS=false

型定義ファイルの作成

src/vite-env.d.ts(またはsrc/env.d.ts)にImportMetaEnvを拡張します。

src/vite-env.d.ts
/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_API_BASE_URL: string;
  readonly VITE_APP_TITLE: string;
  readonly VITE_FEATURE_FLAG_NEW_UI: string; // "true" | "false" の文字列
  readonly VITE_ENABLE_DEVTOOLS: string;
  // 他の環境変数を追加していく...
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

この定義があることでimport.meta.env.VITE_API_BASE_URLstring型として補完されます。

使用例
// 型補完が効く
const apiUrl = import.meta.env.VITE_API_BASE_URL; // string 型
const appTitle = import.meta.env.VITE_APP_TITLE;  // string 型

// 存在しない変数はエラーになる
const unknown = import.meta.env.VITE_UNKNOWN; // TypeScriptエラー(定義されていない)

// boolean に変換して使う
const isNewUiEnabled = import.meta.env.VITE_FEATURE_FLAG_NEW_UI === "true";

// Viteビルトインの環境変数
if (import.meta.env.DEV) {
  console.log("開発モードです");
}
if (import.meta.env.PROD) {
  console.log("本番モードです");
}
Viteビルトイン変数 内容
import.meta.env.MODE string 現在のモード(”development”, “production” など)
import.meta.env.BASE_URL string デプロイのベースURL(vite.configのbaseオプションの値)
import.meta.env.PROD boolean 本番ビルドかどうか
import.meta.env.DEV boolean 開発モードかどうか
import.meta.env.SSR boolean SSRビルドかどうか

型チェックとビルドを分離する

前述の通り、Viteは型チェックをしません。型エラーを見落とさないためにCIパイプラインやpackage.jsonのスクリプトに型チェックを組み込みます。

package.json
{
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "type-check": "tsc --noEmit",
    "lint": "eslint . --ext ts,tsx",
    "preview": "vite preview"
  }
}

tsc -bはProject References(tsconfig.jsonのreferences設定)に対応したビルドコマンドです。tsc --noEmitは型チェックのみ実行しJSファイルを生成しません。

vite-plugin-checkerで開発中も型エラーを確認する
vite-plugin-checkerを使うと開発サーバー実行中にもTypeScriptの型エラーをブラウザのオーバーレイや端末に表示できます。
ターミナル
npm install --save-dev vite-plugin-checker
vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import checker from "vite-plugin-checker";

export default defineConfig({
  plugins: [
    react(),
    checker({
      typescript: true, // TypeScript型チェックを有効化
    }),
  ],
});

Vitestとの統合設定

VitestはViteと共通の設定ファイルで動作するテストランナーです。vite.config.tstestセクションを追加するだけで設定できます。

ターミナル
npm install --save-dev vitest @vitest/ui jsdom @testing-library/react @testing-library/jest-dom
vite.config.ts(Vitest設定を追加)
/// <reference types="vitest" />
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,           // describe, it, expect をインポート不要にする
    environment: "jsdom",    // ブラウザ環境をシミュレート
    setupFiles: "./src/test/setup.ts",
    coverage: {
      provider: "v8",
      reporter: ["text", "json", "html"],
    },
  },
});
src/test/setup.ts
import "@testing-library/jest-dom";
tsconfig.app.json(Vitest型定義を追加)
{
  "compilerOptions": {
    "types": ["vitest/globals"]
  }
}

Vitestでのテストの書き方全般についてはTypeScript Jest・Vitest テスト完全ガイドを参照してください。

実務的なvite.config.tsのサンプル

ここまでの設定をまとめた、実務で使えるフル設定サンプルです。

vite.config.ts(フル設定)
/// <reference types="vitest" />
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react-swc";
import tsconfigPaths from "vite-tsconfig-paths";
import checker from "vite-plugin-checker";
import path from "path";

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), "");

  return {
    plugins: [
      react(),
      tsconfigPaths(),
      checker({ typescript: true }),
    ],

    resolve: {
      alias: {
        "@": path.resolve(__dirname, "./src"),
      },
    },

    server: {
      port: 3000,
      proxy: {
        "/api": {
          target: env.API_TARGET_URL || "http://localhost:8080",
          changeOrigin: true,
          rewrite: (p) => p.replace(/^\/api/, ""),
        },
      },
    },

    build: {
      outDir: "dist",
      sourcemap: mode !== "production",
      rollupOptions: {
        output: {
          manualChunks: {
            vendor: ["react", "react-dom"],
          },
        },
      },
    },

    test: {
      globals: true,
      environment: "jsdom",
      setupFiles: "./src/test/setup.ts",
    },
  };
});

よくあるエラーと解決策

エラー①:Cannot find module ‘path’

エラーメッセージ
Cannot find module 'path' or its corresponding type declarations.
解決策
# @types/node をインストール
npm install --save-dev @types/node
tsconfig.node.json(types に node を追加)
{
  "compilerOptions": {
    "types": ["node"]
  }
}

エラー②:パスエイリアスが実行時に解決されない

症状
// TypeScriptのコンパイルは通るが、Viteの開発サーバーで
// "Failed to resolve import @/components/Button" エラーが発生する
import { Button } from "@/components/Button";
原因と解決策
// 原因: tsconfig.json の paths を設定しただけで
//       vite.config.ts の resolve.alias が設定されていない

// 解決策①: vite.config.ts に resolve.alias を追加する
resolve: {
  alias: { "@": path.resolve(__dirname, "./src") },
},

// 解決策②: vite-tsconfig-paths プラグインを使う
// tsconfig.json の paths が自動でViteに適用される
plugins: [react(), tsconfigPaths()],

エラー③:import.meta.envの型が効かない

症状
// import.meta.env.VITE_API_URL が any または string になる
const url = import.meta.env.VITE_API_URL;
//                              ^^^^^^^^^^^^^^ 型補完が効かない
解決策
// src/vite-env.d.ts に ImportMetaEnv の拡張を追加する
interface ImportMetaEnv {
  readonly VITE_API_URL: string;
}

// tsconfig.app.json の include に src が含まれていることも確認する
// "include": ["src"]

エラー④:const enumがエラーになる

エラーメッセージ
Cannot access ambient const enums when 'isolatedModules' is enabled.
解決策
// NG: const enum は isolatedModules: true と非互換
const enum Direction {
  Up, Down, Left, Right
}

// OK①: 通常の enum に変更する
enum Direction {
  Up, Down, Left, Right
}

// OK②: union型やオブジェクトで代替する
const Direction = { Up: "up", Down: "down", Left: "left", Right: "right" } as const;
type Direction = typeof Direction[keyof typeof Direction];

const enumとその代替についてはTypeScript enum 完全ガイドを参照してください。

エラー⑤:defineConfigの型エラー

エラーメッセージ
Type '{ test: { globals: boolean; ... } }' is not assignable to type 'UserConfigExport'.
解決策
// 解決策: vite.config.ts の先頭に vitest の型参照を追加する
/// <reference types="vitest" />
import { defineConfig } from "vite";

// これがないと test: {} プロパティが UserConfig の型に存在せずエラーになる

まとめ

ViteとTypeScriptの設定における重要ポイントをまとめます。

項目 ポイント
型チェック Viteは型チェックしない。tsc --noEmitをCIやビルドスクリプトに組み込む
tsconfig Vite v5以降はtsconfig.app.jsontsconfig.node.jsonを分離。moduleResolution: "bundler"が推奨
パスエイリアス tsconfig のpathsとvite.configのresolve.aliasの両方が必要。vite-tsconfig-pathsで一元管理可能
環境変数 VITE_プレフィックスでクライアントに公開。ImportMetaEnvを拡張して型補完を有効化
Vitest vite.config.tstestセクションで設定。/// <reference types="vitest" />が必要
const enum isolatedModules: trueと非互換。通常のenumかunion型で代替

ViteはTypeScriptプロジェクトのビルドツールとして現在最もおすすめできる選択肢の一つです。設定ファイル自体が型安全に書けるのは大きなメリットで、チーム開発でも設定ミスを防ぎやすくなります。

ESLintやPrettierとの組み合わせについてはTypeScript ESLint + Prettier 完全セットアップガイドを、モジュールシステムの詳細についてはTypeScript モジュールとimport/export完全ガイドもあわせてご覧ください。