@the-arj/env-types
v0.1.0
Published
Zero-schema TypeScript type generation and runtime validation from .env files. Auto-generates env.d.ts from .env.example — no schema duplication.
Maintainers
Readme
env-types
Zero-schema TypeScript types from your
.env.example— auto-generated, no duplication.
# Add to .env.example once, get types everywhere
npx @the-arj/env-types generate// process.env is now fully typed — no schema to write
process.env.DATABASE_URL // string
process.env.PORT // string
process.env.SENTRY_DSN // string | undefined (was empty in .env.example)
process.env.TYPO_HERE // ✗ TypeScript error — doesn't existThe Problem
Every other solution makes you write the schema yourself:
// t3-env, envalid, zod — you duplicate your .env:
const env = createEnv({
server: {
DATABASE_URL: z.string().url(), // ← you already wrote this in .env.example
PORT: z.coerce.number(), // ← why write it twice?
}
})env-types reads .env.example and generates the types for you. No schema, no duplication.
Install
npm install -D @the-arj/env-typesQuick Start
1. Add to package.json scripts:
{
"scripts": {
"env:generate": "env-types generate",
"env:validate": "env-types validate"
}
}2. Generate types:
npm run env:generateThis reads .env.example and writes env.d.ts:
// env.d.ts — auto-generated by env-types
declare namespace NodeJS {
interface ProcessEnv {
/** App config */
NODE_ENV: string
/** @type {number string} */
PORT: string
/** Database */
DATABASE_URL: string
/** @optional */
SENTRY_DSN?: string
}
}3. Validate before deploy:
npm run env:validate
# ✓ All 4 required variables are set.
# — or —
# ✗ 2 missing variables:
# • DATABASE_URL: Required variable "DATABASE_URL" is missing or empty
# • SECRET_KEY: Required variable "SECRET_KEY" is missing or emptyCLI Reference
env-types generate
Generate env.d.ts from .env.example.
env-types generate
env-types generate --input .env --output src/env.d.ts
env-types generate --watch # re-generate on file change
env-types generate --scaffold # generate a loadEnv() schema instead| Flag | Default | Description |
|---|---|---|
| --input | .env.example | Source .env file |
| --output | env.d.ts | Output .d.ts file |
| --watch | — | Watch and re-generate on change |
| --scaffold | — | Output a loadEnv() schema instead of .d.ts |
env-types validate
Check that all required variables from .env.example are set in the current environment.
env-types validate
env-types validate --input .env.exampleExits with code 1 if any required variables are missing — safe to use in CI.
Library API
loadEnv(schema, source?)
Runtime-safe, typed env loading with coercion. Use when you want numbers/booleans instead of raw strings.
import { loadEnv } from '@the-arj/env-types'
const env = loadEnv({
PORT: { type: 'number' },
NODE_ENV: { type: 'string' },
DEBUG: { type: 'boolean', required: false, default: false },
DATABASE_URL: { type: 'url' },
})
env.PORT // number ✓
env.DEBUG // boolean ✓Throws a consolidated error listing all missing/invalid variables at once.
Supported types
| Type | Input | Output |
|---|---|---|
| 'string' | any string | string |
| 'number' | "3000" | 3000 |
| 'boolean' | "true" / "false" | true / false |
| 'url' | "https://..." | string (validated) |
| 'json' | '{"a":1}' | unknown (parsed) |
parseEnvFile(content)
Parse a .env file string into structured entries.
import { parseEnvFile } from '@the-arj/env-types'
const entries = parseEnvFile(`
# Server port
PORT=3000
DATABASE_URL=postgres://localhost/db
OPTIONAL=
`)
// entries[0] → { key: 'PORT', rawValue: '3000', isOptional: false, inferredType: 'number', description: 'Server port' }generateDts(entries, options?)
Generate a .d.ts string from parsed entries.
import { parseEnvFile, generateDts } from '@the-arj/env-types'
import { readFileSync, writeFileSync } from 'node:fs'
const content = readFileSync('.env.example', 'utf8')
const entries = parseEnvFile(content)
writeFileSync('env.d.ts', generateDts(entries))validateAgainstEntries(entries, source?)
Validate a source object against parsed entries. Returns an array of ValidationError objects (empty = valid).
import { parseEnvFile, validateAgainstEntries } from '@the-arj/env-types'
const entries = parseEnvFile(readFileSync('.env.example', 'utf8'))
const errors = validateAgainstEntries(entries, process.env)
if (errors.length > 0) {
for (const e of errors) console.error(`Missing: ${e.key}`)
process.exit(1)
}Type Inference Rules
| Value in .env.example | inferredType | Optional? |
|---|---|---|
| PORT=3000 | number | no |
| DEBUG=true | boolean | no |
| URL=https://api.example.com | url | no |
| CONFIG={"a":1} | json | no |
| NODE_ENV=development | string | no |
| OPTIONAL= | string | yes |
All
ProcessEnvvalues arestring— type hints are informational JSDoc only. UseloadEnv()for actual coercion.
Recommended Workflow
.env.example → env-types generate → env.d.ts → TypeScript knows your vars
env-types validate → CI fails fast on missing varsAdd to your postinstall or CI setup step:
{
"scripts": {
"postinstall": "env-types generate"
}
}License
MIT © The-ARJ
