Skip to content

objectenvy / envy

Function: envy()

ts
function envy<T>(config): { [KeyType in string | number | symbol]: UnionToIntersection<[T] extends [unknown[]] ? never : [T] extends [object] ? { [K in string]: [T[K]] extends [unknown[]] ? `${ScreamingSnakeCase<(...), (...)>}` extends "" ? never : Record<`${(...)}`, string> : [(...)[(...)]] extends [object] ? { [K in (...)]: (...) }[(...) & (...)] : [(...)] extends [(...)] ? (...) extends (...) ? (...) : (...) : never }[keyof T & string] : [T] extends [Primitive] ? never : never>[KeyType] };

Defined in: objectEnvy.ts:984

Serialize a nested camelCased config object back to a flat SCREAMING_SNAKE_CASE env record.

Type Parameters

Type Parameter
T extends ConfigObject

Parameters

ParameterTypeDescription
configTA nested camelCased configuration object.

Returns

{ [KeyType in string | number | symbol]: UnionToIntersection<[T] extends [unknown[]] ? never : [T] extends [object] ? { [K in string]: [T[K]] extends [unknown[]] ? `${ScreamingSnakeCase<(...), (...)>}` extends "" ? never : Record<`${(...)}`, string> : [(...)[(...)]] extends [object] ? { [K in (...)]: (...) }[(...) & (...)] : [(...)] extends [(...)] ? (...) extends (...) ? (...) : (...) : never }[keyof T & string] : [T] extends [Primitive] ? never : never>[KeyType] }

A flat Record<string, string> with SCREAMING_SNAKE_CASE keys and all values stringified.

Remarks

envy is the inverse of objectify: it flattens a nested config tree by joining each key path with underscores and uppercasing the result. All values are stringified — numbers and booleans become their string representations. Arrays are serialized as comma-separated strings (e.g., ['a', 'b']'a,b'). Object items inside arrays are JSON-serialized before joining.

The return type is ToEnv<T>, which preserves string literal and template literal types from the config type all the way into the env record type.

Use When

  • You need to spawn a child process and want to pass typed config as env variables.
  • You're writing a .env file from a config object (e.g., for CI scaffolding or test fixtures).
  • You use ToEnv<T> for compile-time validation and need the runtime values to match.
  • You're round-tripping: objectify() → mutate config → envy() → write back to env.

Avoid When

  • You only need the ToEnv<T> type at compile time — no need to call envy() at runtime.
  • The config contains Date, Map, Set, or class instances — envy() serializes them as [object Object] via String().

Pitfalls

  • NEVER rely on envy() to round-trip arrays of objects faithfully — BECAUSE object items are JSON.stringify-ed then joined; when objectify() re-reads the comma-separated string, it treats it as a string array, not an array of objects.
  • NEVER pass null or undefined values in the config — BECAUSE envy() silently skips null/undefined entries, leaving no env key for them; the round-trip loses those fields.
  • NEVER expect envy() to honour a prefix — BECAUSE it outputs bare SCREAMING_SNAKE_CASE keys with no prefix. Add the prefix yourself if your deployment expects APP_PORT rather than PORT.

Examples

ts
import { envy } from 'objectenvy';

const config = {
  portNumber: 3000,
  log: { level: 'debug', path: '/var/log' }
};
const env = envy(config);
// { PORT_NUMBER: '3000', LOG_LEVEL: 'debug', LOG_PATH: '/var/log' }
ts
// Round-trip: objectify → mutate → envy
import { objectify, envy } from 'objectenvy';
const config = objectify({ env: process.env, prefix: 'APP' });
const mutated = { ...config, debug: true };
const newEnv = envy(mutated);
// spawn({ env: { ...process.env, ...newEnv } })
ts
// Array values are joined as comma-separated strings
import { envy } from 'objectenvy';
const config = { hosts: ['localhost', 'example.com'] };
const env = envy(config);
// { HOSTS: 'localhost,example.com' }

See

  • objectify for the inverse operation (env → config)
  • ToEnv for the compile-time type utility

Released under the MIT License.