TypeScript 中 process.env 的 string | undefined 问题

1. 问题场景

在 Node.js 中,可以通过 process.env 读取环境变量。例如,在 db.ts 中使用环境变量创建 Sequelize 实例:

import "dotenv/config";
import { Sequelize } from "sequelize";

const db = new Sequelize({
  database: process.env.DATABASE,
  host: process.env.HOST,
  username: process.env.USER_NAME,
  password: process.env.PASSWORD,
  dialect: "mysql",
});

export default db;

开启 TypeScript 严格检查后,这些配置项可能出现类型错误。原因是 TypeScript 将环境变量统一定义为:

string | undefined

即使 .env 文件中已经写了对应变量,TypeScript 在编译阶段也不会读取 .env 并证明变量一定存在。对 TypeScript 来说,下面两种情况都有可能发生:

process.env.DATABASE; // string:变量存在
process.env.DATABASE; // undefined:变量不存在

而 Sequelize 的数据库名称、用户名等配置通常要求确定的 string,所以 string | undefined 不能直接当作 string 使用。

2. 方式一:启动时校验环境变量(推荐)

可以编写一个函数,在程序启动时读取并检查必需的环境变量:

import "dotenv/config";
import { Sequelize } from "sequelize";

function getRequiredEnv(name: string): string {
  const value = process.env[name];

  if (value === undefined || value === "") {
    throw new Error(`缺少必需的环境变量:${name}`);
  }

  return value;
}

const db = new Sequelize({
  database: getRequiredEnv("DATABASE"),
  host: getRequiredEnv("HOST"),
  username: getRequiredEnv("USER_NAME"),
  password: getRequiredEnv("PASSWORD"),
  dialect: "mysql",
});

export default db;

为什么校验后是 string

在判断语句之前,value 的类型是:

string | undefined

如果值是 undefined 或空字符串,函数会抛出错误,后面的代码不会继续执行。因此,能够运行到 return value 时,TypeScript 可以确定 value 是一个非空字符串。

优点

适用场景

适用于数据库名、账号、密码等程序正常运行所必需的配置,是当前项目最推荐的处理方式。

3. 方式二:使用非空断言 !

如果确定运行环境中一定存在这些变量,可以使用非空断言:

import "dotenv/config";
import { Sequelize } from "sequelize";

const db = new Sequelize({
  database: process.env.DATABASE!,
  host: process.env.HOST!,
  username: process.env.USER_NAME!,
  password: process.env.PASSWORD!,
  dialect: "mysql",
});

export default db;

! 是在告诉 TypeScript:

我可以保证这个值不是 null 或 undefined,请把它当作确定的字符串处理。

它只会改变 TypeScript 对值的判断,不会产生运行时校验代码,也不会自动补充缺失的环境变量。

例如,下面的代码可以通过类型检查:

const database: string = process.env.DATABASE!;

但是如果 DATABASE 实际不存在,运行时得到的仍然是 undefined。

优点

风险

适用场景

适合简单示例,或者环境变量已经由部署平台、配置中心等外部机制可靠校验的场景。

4. 方式三:使用默认值 ??

如果某个环境变量确实允许缺省,可以使用空值合并运算符提供默认值:

import "dotenv/config";
import { Sequelize } from "sequelize";

const db = new Sequelize({
  database: process.env.DATABASE ?? "nloop",
  host: process.env.HOST ?? "localhost",
  username: process.env.USER_NAME ?? "root",
  password: process.env.PASSWORD ?? "",
  dialect: "mysql",
});

export default db;

下面的表达式表示:如果 HOST 是 null 或 undefined,就使用 "localhost"。

process.env.HOST ?? "localhost"

无论环境变量是否存在,整个表达式最终都会得到一个字符串,因此可以满足 Sequelize 的配置类型要求。

?? 和 || 的区别

?? 只在左侧是 null 或 undefined 时使用默认值:

const value = process.env.PORT ?? "8080";

|| 则会把空字符串等所有假值都替换为默认值:

const value = process.env.PORT || "8080";

处理环境变量时,应根据空字符串是否有效来选择。通常 ?? 更准确地表达“变量没有定义时才使用默认值”。

优点

风险

适用场景

适用于缺少配置时确实可以安全使用约定值的选项,例如本地开发环境中的 HOST=localhost。

5. 三种方式对比

方式 解决类型错误 运行时检查 变量缺失时的行为 推荐程度
启动时校验 是 是 立即抛出明确错误 推荐
非空断言 ! 是 否 值仍可能是 undefined 谨慎使用
默认值 ?? 是 提供备用值 使用指定的默认值 按配置项使用

6. 当前项目的建议

对于当前 Sequelize 配置,可以按配置的性质分别处理:

关键原则是:不要只考虑如何让红色类型提示消失,还要明确环境变量缺失时,程序应该立即报错还是采用安全的默认值。