TypeScriptで環境変数に型を付ける方法
process.env.API_KEYを100回目に書いても、TypeScriptは依然としてstring | undefinedとして型付けします。IDEは自動補完を提供しません。さらに悪いことに、本番環境にデプロイした後、変数が欠落していてアプリがクラッシュすることを発見します。TypeScriptの環境変数は、このような扱いよりも優れた処理に値します。
このガイドでは、モダンなフロントエンドおよびフルスタックプロジェクトで環境変数に型安全性を追加する方法を示します。Viteベースのセットアップ向けのimport.meta.envと、Nodeコンテキスト向けのprocess.envの両方をカバーします。
重要なポイント
- ブラウザアプリはビルド時注入(バンドルに焼き込まれる変数)を使用し、Node.jsは実行時に環境変数を読み取ります。この違いはセキュリティと型付け戦略の両方に影響します。
- ViteプロジェクトではIDEの自動補完と型チェックを得るために
ImportMetaEnv宣言を使用し、NodeコンテキストではNodeJS.ProcessEnvの拡張を使用します。 - TypeScriptの型だけでは実行時のクラッシュを防げません。常に起動時に簡単なチェックやZodのようなスキーマライブラリを使用して必須の環境変数を検証してください。
- プレフィックス付き変数(
VITE_、NEXT_PUBLIC_)にシークレットを入れないでください。これらはクライアント側のバンドルで見えるようになります。
ビルド時環境変数と実行時環境変数の理解
何かを型付けする前に、重要な違いを理解してください。ブラウザアプリとサーバーは環境変数を異なる方法で処理します。
ビルド時注入(ブラウザアプリ): Viteのようなツールは、ビルド中に環境変数の参照を実際の値に置き換えます。変数は実行時には存在せず、JavaScriptバンドルに焼き込まれます。
実行時環境変数(サーバー側): Node.jsは、コードが実行されるときに実際のシステム環境からprocess.envを読み取ります。値は再ビルドなしでデプロイメント間で変更できます。
この違いはセキュリティにとって重要です。ビルド時に注入された変数はすべて、クライアント側のコードで見えるようになります。そのため、フレームワークはプレフィックスベースの公開ルールを使用します。ViteはVITE_で始まる変数のみを公開し、Next.jsはNEXT_PUBLIC_プレフィックスを持つクライアント側変数を公開します。これらのプレフィックスを持たないプライベートキーはサーバー側のみに留まります。
ViteプロジェクトでのImport.meta.envの型付け
Viteはprocess.envの代わりにimport.meta.envを使用します。TypeScriptで型安全な環境変数を追加するには、宣言ファイルを作成します:
// env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string
readonly VITE_APP_TITLE: string
// ここにさらに変数を追加
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
これでTypeScriptは自動補完を提供し、これらをstring | undefinedではなくstringとして扱います。このファイルをsrcディレクトリに配置し、tsconfig.jsonがそれを含むようにしてください。
Node.jsコンテキストでのProcessEnvの型付け
サーバー側のコードやprocess.envを使用するツールの場合、NodeJS.ProcessEnvインターフェースを拡張します:
// globals.d.ts
declare namespace NodeJS {
interface ProcessEnv {
DATABASE_URL: string
API_SECRET: string
NODE_ENV: 'development' | 'production' | 'test'
}
}
このアプローチにより、コードベース全体で自動補完が得られます。NODE_ENVの例は、既知の可能な値を持つ変数にユニオン型を使用できることを示しています。
Discover how at OpenReplay.com.
型だけでは不十分な理由
ここに落とし穴があります。宣言のマージはTypeScriptに何が存在すべきかを伝えますが、何が実際に存在するかを伝えません。DATABASE_URLをstringとして型付けしましたが、誰かが設定を忘れると、アプリは実行時にクラッシュします。
TypeScriptの型はコンパイル時に消去されます。コードが実行されるときに環境変数が実際に存在することを検証できません。
起動時の環境変数の検証
TypeScriptで環境変数を検証するには、アプリが重要なことを行う前に早期にチェックします。シンプルなアプローチ:
// config.ts
function getEnvVar(key: string): string {
const value = process.env[key]
if (!value) {
throw new Error(`Missing required environment variable: ${key}`)
}
return value
}
export const config = {
databaseUrl: getEnvVar('DATABASE_URL'),
apiSecret: getEnvVar('API_SECRET'),
} as const
アプリのエントリーポイントでこのconfigモジュールをインポートします。変数が欠落している場合、リクエストの途中で発見するのではなく、すぐに知ることができます。
より堅牢な検証のために、Zodのようなライブラリを使用すると、型、フォーマット、制約を検証するスキーマを定義できます:
import { z } from 'zod'
const envSchema = z.object({
DATABASE_URL: z.string().url(),
PORT: z.string().transform(Number).pipe(z.number().min(1)),
})
export const env = envSchema.parse(process.env)
これは、DATABASE_URLが有効なURLでない場合やPORTが数値でない場合に、明確なエラーメッセージで即座に失敗します。
変数のセキュリティ保持
プレフィックスルールを覚えておいてください。Viteプロジェクトでは、VITE_プレフィックスが付いた変数のみがブラウザに到達します。それ以外はすべてビルドプロセス(サーバー側)でのみ利用可能で、ブラウザには送信されません。プレフィックス付き変数にシークレットを入れないでください。本番バンドルで見えるようになります。
サーバー側のシークレットについては、本番環境では.envファイルではなく、ホスティングプラットフォームの環境設定に依存してください。.envを即座に.gitignoreに追加してください。
まとめ
TypeScriptでの型安全な環境変数には2つのレイヤーが必要です。コンパイル時の自動補完のための宣言のマージと、本番環境の安全性のための実行時検証です。ViteプロジェクトではImportMetaEnvを、NodeコンテキストではNodeJS.ProcessEnvを使用し、常に起動時に必須変数を検証してください。午前3時の本番インシデントをデバッグしている未来のあなたが感謝するでしょう。
よくある質問
はい、ただし宣言は分けてください。Viteフロントエンドパッケージ用にImportMetaEnvを含むenv.d.tsファイルを作成し、バックエンドパッケージ用にNodeJS.ProcessEnv拡張を含むglobals.d.tsを作成します。各パッケージのtsconfig.jsonは、型の競合を避けるために関連する宣言ファイルのみを含めるべきです。
tsconfig.jsonが宣言ファイルを含んでいない可能性があります。includeの配列がファイルの場所をカバーしているか確認するか、ファイルパスを明示的に追加してください。また、別の宣言ファイルで誤ってインターフェースをシャドウイングしていないか確認してください。変更後はTypeScriptサーバーを再起動してください。
フロントエンドアプリの場合、変数はビルドプロセス中に焼き込まれるため、ビルド時の検証がより有用です。Viteが実行される前に必須のVITE_変数が存在することを確認するprebuildスクリプトを追加してください。ブラウザでの実行時検証は、いずれにせよ欠落した変数から回復できません。
インターフェース宣言でオプションの変数にクエスチョンマークを付けます。例: OPTIONAL_VAR?: string。検証には、Zodのoptional()メソッドを使用するか、default()でデフォルト値を提供します。configオブジェクトは、どの変数が本当に必須で、どれがあると良い程度かを反映すべきです。
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.