A highly opinionated way to parse env values that I find useful.
Guesses the type of a value from its string form so you can stop writing repetitive parsing logic.
npm install @fossiq/envalimport { enval } from "@fossiq/enval";Booleans (case-insensitive)
enval("true"); // true
enval("YES"); // true
enval("off"); // falseNumbers (with safe integer protection for large numbers like Snowflake IDs)
enval("42"); // 42
enval("3.14"); // 3.14
enval("1234567890123456789"); // "1234567890123456789"Nullish
enval("null"); // null
enval("undefined"); // undefinedJSON
enval("[1, 2, 3]"); // [1, 2, 3]
enval('{"enabled": true}'); // { enabled: true }Strings (auto-unquoted and trimmed)
enval('"hello"'); // "hello"
enval(" text "); // "text"Constructor shorthand
enval('["a", "b", "a"]', Set); // Set(2) { "a", "b" }- Nullish:
null,undefined - Boolean:
true,false,yes,no,on,off(case-insensitive) - Number: Numeric strings (with safe integer protection)
- JSON: Objects
{}and arrays[] - String: Everything else
TypeScript type hints
enval<string[]>('["a", "b"]'); // string[]
enval<{ host: string }>('{"host": "localhost"}'); // { host: string }Optional values with defaults
const maxRetries = enval<number>(process.env.MAX_RETRIES) ?? 3;Comma-separated values (when JSON arrays aren't an option)
const ports = enval("3000,3001,3002", (_, raw) =>
(raw as string).split(",").map(Number),
); // [3000, 3001, 3002]Validation at parse time
const port = enval(process.env.PORT, (inferred) => {
const num = inferred as number;
if (num < 1 || num > 65535) throw new Error(`Invalid port: ${num}`);
return num;
});