还在手写 env 类型定义?这个 Vite 插件帮你自动搞定!

4 阅读5分钟

项目地址:GitHub - vite-plugin-typed-env 欢迎提 Issue 和 Star ⭐

痛点:环境变量的类型噩梦

每个前端项目都有 .env 文件,里面塞满了各种配置:

VITE_API_URL=https://api.example.com
VITE_PORT=3000
VITE_DEBUG=true
VITE_ALLOWED_ORIGINS=http://localhost,https://example.com

然后在 vite-env.d.ts 里手写类型:

interface ImportMetaEnv {
  readonly VITE_API_URL: string
  readonly VITE_PORT: string  // 哦不,这应该是 number!
  readonly VITE_DEBUG: string // 这应该是 boolean...
  readonly VITE_ALLOWED_ORIGINS: string // 应该是 string[]?
}

问题来了:

  1. .env 改了,类型定义忘了改 → 类型不匹配,bug 悄悄溜进来
  2. VITE_PORT=3000 明明是数字,TypeScript 却认为是字符串
  3. 想做运行时校验?还得手写 Zod schema
  4. 新加的环境变量忘记声明,TypeScript 不报错,但运行时可能炸

:::tip 核心问题 类型定义和 .env 文件是两套东西,人工维护它们的一致性 = 定时炸弹 :::

解决方案:vite-plugin-typed-env

我开发了一个 Vite 插件,自动从 .env 文件生成 TypeScript 类型定义和 Zod schema

一句话概括:.env,剩下的交给插件

// vite.config.ts
import envTs from 'vite-plugin-typed-env'

export default defineConfig({
  plugins: [envTs()]
})

就这样,插件会自动生成:

  • env.d.ts - TypeScript 类型声明
  • env.schema.ts - Zod 校验 schema
  • env.ts - 运行时 loader(带校验)

核心能力一览

1. 智能类型推断

插件会根据值自动推断类型:

你的 .env 值推断出的 TypeScript 类型
PORT=3000number
DEBUG=trueboolean
API_URL=https://...string(带 URL 校验)
ALLOWED=1,2,3number[]
ORIGINS=a,b,cstring[]

生成的类型定义:

interface ImportMetaEnv {
  readonly PORT: number
  readonly DEBUG: boolean
  readonly API_URL: string
  readonly ALLOWED: number[]
  readonly ORIGINS: string[]
}

2. 注释指令控制

如果自动推断不够精准,可以用注释指令:

# @type: port
# @desc: 服务监听端口
PORT=3000

# @type: enum(info, warn, error)
LOG_LEVEL=info

# @optional
# @type: url
SENTRY_DSN=

# @type: string[]
ALLOWED_ORIGINS=http://localhost,https://example.com

生成结果:

interface ImportMetaEnv {
  /** 服务监听端口 */
  readonly PORT: number  // zod: z.coerce.number().int().min(1).max(65535)
  readonly LOG_LEVEL: 'info' | 'warn' | 'error'
  readonly SENTRY_DSN?: string  // optional + URL 校验
  readonly ALLOWED_ORIGINS: string[]
}

支持的注释指令:

指令用途
@type: number强制数字类型
@type: boolean强制布尔类型
@type: urlURL 格式校验
@type: port端口号校验(1-65535)
@type: email邮箱格式校验
@type: string[]字符串数组
@type: number[]数字数组
@type: enum(a,b,c)联合类型枚举
@optional标记为可选
@default: 8080设置默认值
@desc: 描述添加 JSDoc 注释

3. Zod Schema 自动生成

生成的 env.schema.ts

import { z } from 'zod'

export const envSchema = z.object({
  PORT: z.coerce.number().int().min(1).max(65535),
  LOG_LEVEL: z.enum(['info', 'warn', 'error']),
  SENTRY_DSN: z.string().url().optional(),
  ALLOWED_ORIGINS: z.string().transform((v) => v.split(',').map((s) => s.trim()))
})

export type Env = z.infer<typeof envSchema>

4. 运行时校验 Loader

生成的 env.ts

import { envSchema } from './env.schema'

const _parsed = envSchema.safeParse(import.meta.env)

if (!_parsed.success) {
  throw new Error('[vite-plugin-typed-env] Invalid environment variables')
}

export const env = _parsed.data
export default env

使用方式:

import env from './env'

// 完全类型安全,且有运行时校验保障
console.log(env.PORT)      // number
console.log(env.LOG_LEVEL) // 'info' | 'warn' | 'error'

5. 热更新支持

开发模式下,修改 .env 文件会自动重新生成类型文件,并触发 Vite 热更新。

6. import.meta.env 类型增强

默认开启,import.meta.env 自动获得完整类型:

// 这也有类型了!
const port = import.meta.env.PORT  // number,不是 string

快速上手

安装

npm install vite-plugin-typed-env -D
npm install zod  # 如果使用 Zod 校验(默认开启)

配置

// vite.config.ts
import envTs from 'vite-plugin-typed-env'

export default defineConfig({
  plugins: [envTs()]
})

写 .env

# 数据库配置
DATABASE_URL=postgres://localhost:5432/mydb

# API 密钥(可选)
# @optional
API_KEY=

# 服务端口
# @type: port
# @desc: 服务监听端口
PORT=3000

# 调试模式
# @type: boolean
DEBUG=true

# 允许的跨域来源
# @type: string[]
ALLOWED_ORIGINS=http://localhost,https://example.com

使用

// 方式一:带运行时校验
import env from './env'

console.log(env.PORT) // fully typed!

// 方式二:Vite 原生方式
console.log(import.meta.env.PORT) // 同样有类型!

配置选项

envTs({
  // 输出目录(相对于项目根目录)
  output: 'src',

  // 是否生成 Zod schema
  schema: 'zod' | false,

  // 是否增强 import.meta.env 类型
  augmentImportMeta: true,

  // 缺失必填变量时是否报错
  strict: true,

  // 额外监听的 .env 文件
  envFiles: ['.env.custom']
})

与现有方案对比

方案类型定义运行时校验自动同步热更新
手写 vite-env.d.ts
@types/node process.env
dotenv + 手写 schema
vite-plugin-typed-env

工作原理简图

┌─────────────────────────────────────────────────────────────┐
│                         .env 文件                            │
│  PORT=3000                                                  │
│  # @type: boolean                                           │DEBUG=true                                                 │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│                    vite-plugin-typed-env                     │
│                                                             │
│  1. 解析 .env 文件                                           │
│  2. 解析注释指令                                             │
│  3. 智能类型推断                                             │
│  4. 生成类型文件                                             │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│                      生成三个文件                             │
│                                                             │
│  env.d.ts        → TypeScript 类型声明                       │
│  env.schema.ts   → Zod 校验 schema                          │
│  env.ts          → 运行时 loader(带校验)                    │
└─────────────────────────────────────────────────────────────┘

常见问题 FAQ

Q: 支持哪些 Vite 版本?

支持 Vite 4.x 及以上版本。

Q: Zod 是必须的吗?

不是必须的。设置 schema: false 可以跳过 Zod schema 生成,只生成类型定义。

Q: 如何处理多环境?

插件会按优先级自动合并: .env.env.local.env.{NODE_ENV}.env.{NODE_ENV}.local

后面的文件会覆盖前面的同名变量。

Q: 生产环境变量缺失会怎样?

strict: true(默认)模式下,生产构建会失败并报错。开发模式下只警告。

Q: 支持非 Vite 项目吗?

目前只支持 Vite。如果你用其他构建工具,可以参考源码思路自行实现。

项目信息


欢迎参与

如果你觉得这个插件有用,欢迎:

  • ⭐ Star 支持
  • 🐛 提 Issue 反馈问题
  • 🔧 提 PR 贡献代码

项目刚刚发布,还有很多可以改进的地方。如果你有好的想法,欢迎来聊!

你的 Star 是开源作者最大的动力 ⭐