Q25 of 38 · TypeScript
How do you type environment variables safely in a TypeScript test project?
Short answer
Short answer: Access `process.env.VAR_NAME` and validate at startup — never trust individual accesses to return a defined string. Create a typed config module that reads env vars, throws on missing required values, and exports a typed `config` object. This centralizes validation and gives the rest of the project typed access.
Detail
process.env is typed as Record<string, string | undefined> in Node.js TypeScript — every access can be undefined. This is correct, but it means you must handle undefined on every access unless you centralize the concern.
Pattern: typed config module:
- Read all env vars in one file at startup.
- Validate required vars are defined; throw if not.
- Export a typed config object — the rest of the codebase uses this, not
process.envdirectly.
Non-null assertion on env vars: process.env.BASE_URL! suppresses the undefined error but crashes at runtime if the var is actually missing. Worse than no protection. Always validate first.
dotenv: The dotenv package loads .env files into process.env. For tests, use dotenv.config() in your global setup. Never commit .env files — use .env.example.
Type augmentation for custom env: If you want TypeScript to know about your specific env var names, augment the ProcessEnv interface:
declare global { namespace NodeJS { interface ProcessEnv { BASE_URL: string; API_KEY: string; } } }
Note: this gives all vars the type string (not string | undefined) — combine with a validation function for safety.
// EXAMPLE
// config.ts — centralized env validation
function requireEnv(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`Missing required env var: ${name}`);
return value;
}
export const config = {
baseUrl: requireEnv("BASE_URL"),
apiKey: requireEnv("API_KEY"),
environment: process.env.TEST_ENV ?? "staging", // optional with default
headless: process.env.HEADLESS !== "false", // boolean from string
} as const;
// rest of tests use config.baseUrl — never process.env directly
import { config } from "./config";
await page.goto(config.baseUrl);
// Optional: augment ProcessEnv for IDE autocomplete
declare global {
namespace NodeJS {
interface ProcessEnv {
BASE_URL: string;
API_KEY: string;
TEST_ENV?: string;
HEADLESS?: string;
}
}
}