1

在TS的项目开发中,经常会出现process.env无法自动补齐的情况。

console.log(process.env.MY_ENV_VARIABLE); // 不会自动补齐

而且会被推断成string或者undefined。

如果你要把它作为string传给一个函数,那你就必须做一下类型转换。哪怕你知道它一定会存在,都必须这样子做。不然会报错。

那如何解决呢?

方案1:增强全局类型

你可以增强全局类型NodeJS.ProcessEnv,以包含你的环境变量:

// globals.d.ts
namespace NodeJS {
  interface ProcessEnv {
    MY_ENV_VARIABLE: string;
  }
}

这依赖于全局接口NodeJS.ProcessEnv上的声明合并,添加一个额外的属性MY_ENV_VARIABLE。

现在,在项目中的每个文件中,你将获得MY_ENV_VARIABLE的自动完成,它将以字符串的形式键入。

然而,这并不能保证环境变量确实存在于你的系统中。也许它真的不存在。

方案2:封装获取方法

为了确保程序在启动时不会因为缺少关键环境变量而崩溃,可以对必需的变量进行校验。例如,可以编写一个专门的配置模块,读取并验证所有需要的环境变量。

const getEnv = (key: keyof NodeJS.ProcessEnv, defaultValue?: string): string => {
  const value = process.env[key];
  if (!value && !defaultValue) {
    throw new Error(`Environment variable ${key} is missing`);
  }
  return value || defaultValue!;
};

export const config = {
  apiUrl: getEnv('API_URL'),
  port: getEnv('PORT', '3000'),
  databaseUrl: getEnv('DATABASE_URL'),
};

这种方法主要是通过通用的条件判断消除undefined的情况,不仅提供了环境变量的类型安全性,还能在项目启动时进行严格的环境变量校验。

方案3:使用t3-env在运行时验证它

如果你想验证所有环境变量在运行时都存在,你可以使用t3-env等库。

t3-env 是 T3 Stack 中的一个用于管理和验证环境变量的库。它的主要目的是在 TypeScript 项目中增强环境变量的类型安全和一致性。

t3-env 的核心目标是确保在项目中使用的环境变量具备以下特性:

  • 类型安全:通过类型定义确保环境变量的类型正确,避免开发过程中出现类型错误。
  • 运行时验证:在项目启动时对环境变量进行验证,确保必需的变量已经定义且格式正确。
  • 环境隔离:确保开发、生产等环境使用的环境变量不会混淆或出现错误配置。

t3-env 基于 Zod 库,它提供了 schema 定义功能,可以对环境变量进行严格的类型验证。这里有一个例子:

import { createEnv } from "@t3-oss/env-core";
import { z } from "zod";

export const env = createEnv({
  server: {
    DATABASE_URL: z.string().url(),
    OPEN_AI_API_KEY: z.string().min(1),
  },
  clientPrefix: "PUBLIC_",
  client: {
    PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
  },
  runtimeEnv: process.env,
});

如果任何环境变量缺失或与模式不匹配,这将在运行时失败。Zod 会抛出详细的错误报告,显示哪个环境变量不匹配以及出现了什么问题。

Zod拥有强大的验证功能。

  • 字符串验证:z.string() 用于指定一个环境变量必须是字符串类型。
  • URL 验证:z.string().url() 确保一个字符串是合法的 URL。
  • 枚举类型:z.enum() 可以限制一个环境变量的值必须在一组选定的值之内。
  • 正则表达式匹配:z.string().regex() 用于对字符串进行正则验证,如验证 PORT 变量是否为数字。
  • 可选字段:z.string().optional() 表示这个变量是可选的。

也可以根据自己的需求自定义环境变量的验证规则,例如你需要确保 API_TIMEOUT 是一个数字,并且在 1 到 10 之间,你可以这样定义:

export const customSchema = z.object({
  API_TIMEOUT: z.string().transform(Number).refine(val => val >= 1 && val <= 10, {
    message: "API_TIMEOUT must be between 1 and 10",
  }),
});

在这个示例中,API_TIMEOUT 被转换成 Number 类型,并且通过 refine() 方法确保它的值在 1 到 10 之间。

它还为你的环境变量提供了单一的真实来源-env。这意味着你可以在整个代码库中使用env,你将获得环境变量的自动完成和类型检查。

哪种方案更好?

如果你的项目很小,并且你不想添加额外的依赖项,则增强全局类型是一个很好的解决方案。

但是,如果你担心运行时缺少环境变量,t3-env是一个不错的选择。

本文由mdnice多平台发布


Miniwa
29 声望1 粉丝