Skip to content

objectenvy / override

Function: override()

ts
function override<T>(
   defaults, 
   config, 
   options?): T;

Defined in: objectEnvy.ts:789

Apply default values to a config object, filling in only the keys that are absent in config.

Type Parameters

Type Parameter
T extends ConfigObject

Parameters

ParameterTypeDescription
defaultsTThe base values to fall back to for missing keys.
configPartial<T>The user-supplied values; these always take precedence over defaults.
optionsMergeOptionsMerge options, including arrayMergeStrategy.

Returns

T

A new object combining config (priority) with any keys absent from config filled from defaults.

Remarks

override is a one-directional merge: config wins. For every key in defaults, if config already has a value for that key it is kept; otherwise the default is used. Nested objects are traversed recursively so deeply-nested defaults are filled in without overwriting any key that config sets at any depth.

Array merging is controlled by options.arrayMergeStrategy:

  • 'replace' (default): the config array replaces the default array entirely.
  • 'concat': config array followed by any remaining defaults array elements.
  • 'concat-unique': same as concat but duplicate primitives are removed.

Use When

  • You want to layer environment config on top of hard-coded application defaults.
  • You have partial user-supplied configs and need safe fallback values for unset fields.
  • You're building a plugin or middleware layer that injects sensible defaults without overriding user intent.

Avoid When

  • You need a symmetric deep merge where neither object has priority — use merge() instead.
  • You need to merge more than two objects at once — chain multiple override() calls.

Pitfalls

  • NEVER mutate the defaults or config arguments after calling override() — BECAUSE the returned object is a shallow copy at each level; nested sub-objects are NOT deep-cloned, so mutations to deeply nested objects propagate back through the shared reference.
  • NEVER rely on override() to handle class instances or special objects (Date, Map, Set) — BECAUSE the function checks typeof === 'object' and recurses, which may produce unexpected results for non-plain-object values.

Examples

ts
import { objectify, override } from 'objectenvy';

const defaults = { port: 3000, log: { level: 'info', path: '/var/log' } };
const envConfig = objectify({ env: process.env, prefix: 'APP' });
const config = override(defaults, envConfig);
// { port: 3000, log: { level: 'debug', path: '/var/log' } }
// env wins where it has values; defaults fill missing keys
ts
// Append default tags when env provides its own list
import { override } from 'objectenvy';
const defaults = { tags: ['v1'] };
const config = { tags: ['prod'] };
const result = override(defaults, config, { arrayMergeStrategy: 'concat' });
// { tags: ['prod', 'v1'] }

See

merge for a symmetric deep merge (neither object has priority)

Default Value

options.arrayMergeStrategy defaults to 'replace'

Released under the MIT License.