Parse environment variables into typed config

TypeScript
// Read environment variables once at startup and fail fast on bad values.
type Config = {
  nodeEnv: 'development' | 'production' | 'test';
  port: number;
  debug: boolean;
  databaseUrl: string;
};

function required(env: Record<string, string | undefined>, name: string): string {
  const value = env[name];
  if (value === undefined || value.trim() === '') throw new Error(`Missing env: ${name}`);
  return value;
}

function toInteger(value: string, name: string): number {
  const parsed = Number(value);
  if (!Number.isInteger(parsed)) throw new Error(`${name} is not an integer: ${value}`);
  return parsed;
}

function loadConfig(env: Record<string, string | undefined> = process.env): Config {
  const nodeEnv = env.NODE_ENV ?? 'development';
  if (!['development', 'production', 'test'].includes(nodeEnv)) {
    throw new Error(`Invalid NODE_ENV: ${nodeEnv}`);
  }
  return {
    nodeEnv: nodeEnv as Config['nodeEnv'],
    port: toInteger(env.PORT ?? '3000', 'PORT'),
    debug: (env.DEBUG ?? 'false') === 'true',
    databaseUrl: required(env, 'DATABASE_URL'),
  };
}

console.log(loadConfig({ NODE_ENV: 'test', PORT: '8080', DATABASE_URL: 'postgres://app' }));