or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

index.mddocs/

defu

defu is a lightweight and fast TypeScript library for recursively assigning default properties to JavaScript objects. It provides multiple merging strategies with left-most argument priority, deep object merging, array concatenation, and built-in security against object pollution.

Package Information

  • Package Name: defu
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install defu

Core Imports

import { defu, createDefu, defuFn, defuArrayFn } from "defu";

For CommonJS:

const { defu, createDefu, defuFn, defuArrayFn } = require("defu");

For default import:

import defu from "defu"; // Same as named import { defu }

Basic Usage

import { defu } from "defu";

// Basic object merging with leftmost priority
const config = defu(
  { database: { host: "localhost" }, debug: true },
  { database: { host: "prod.example.com", port: 5432 }, debug: false }
);
// Result: { database: { host: "localhost", port: 5432 }, debug: true }

// Array concatenation
const options = defu(
  { plugins: ["auth", "cache"] },
  { plugins: ["logger", "monitor"] }
);
// Result: { plugins: ["auth", "cache", "logger", "monitor"] }

Architecture

defu is built around several key patterns:

  • Leftmost Priority: Arguments provided first take precedence over later arguments
  • Deep Merging: Nested objects are recursively merged rather than replaced
  • Array Concatenation: Arrays are combined rather than overwritten
  • Security-First: Automatically skips __proto__ and constructor keys to prevent pollution
  • Immutability: Never modifies source objects, always returns new merged objects
  • Custom Merging: Extensible through custom merger functions for specialized logic

Capabilities

Standard Merging

Core deep merging functionality with leftmost argument priority and array concatenation.

/**
 * Recursively merge objects with leftmost priority
 * @param source - Primary source object (highest priority)
 * @param defaults - Default objects to merge (lower priority)
 * @returns Merged object with combined properties
 */
const defu: <Source extends Input, Defaults extends Array<Input | IgnoredInput>>(
  source: Source | IgnoredInput,
  ...defaults: Defaults
) => Defu<Source, Defaults>;

Usage Examples:

import { defu } from "defu";

// Multiple defaults with priority
const settings = defu(
  { theme: "dark" },           // Highest priority
  { theme: "light", size: 14 }, // Medium priority  
  { theme: "auto", size: 12, font: "Arial" } // Lowest priority
);
// Result: { theme: "dark", size: 14, font: "Arial" }

// Null and undefined handling
const user = defu(
  { name: null, email: "user@example.com" },
  { name: "John Doe", email: "default@example.com", role: "user" }
);
// Result: { name: "John Doe", email: "user@example.com", role: "user" }

Custom Merger Creation

Factory function to create custom defu instances with specialized merging logic.

/**
 * Create a custom defu instance with custom merger logic
 * @param merger - Optional custom merger function
 * @returns Custom defu function with specialized behavior
 */
function createDefu(merger?: Merger): <Source extends Input, Defaults extends Array<Input | IgnoredInput>>(
  source: Source,
  ...defaults: Defaults
) => Defu<Source, Defaults>;

Usage Examples:

import { createDefu } from "defu";

// Add numbers instead of replacing
const addNumbers = createDefu((obj, key, value) => {
  if (typeof obj[key] === "number" && typeof value === "number") {
    obj[key] += value;
    return true; // Indicates custom merge was applied
  }
});

const totals = addNumbers({ cost: 15, items: 3 }, { cost: 10, items: 2 });
// Result: { cost: 25, items: 5 }

// Custom object merging
const mergeArrays = createDefu((obj, key, value) => {
  if (Array.isArray(obj[key]) && Array.isArray(value)) {
    obj[key] = [...new Set([...obj[key], ...value])]; // Unique merge
    return true;
  }
});

Function-Based Value Transformation

Pre-configured defu instance that applies function transformation when source provides functions.

/**
 * Defu instance with function transformation support
 * When source provides a function and default value exists, calls function with default value
 */
const defuFn: <Source extends Input, Defaults extends Array<Input | IgnoredInput>>(
  source: Source,
  ...defaults: Defaults
) => Defu<Source, Defaults>;

Usage Examples:

import { defuFn } from "defu";

// Transform default values using functions
const config = defuFn(
  {
    timeout: (defaultTimeout) => defaultTimeout * 2,
    retries: (defaultRetries) => Math.min(defaultRetries + 2, 10),
    features: (defaultFeatures) => defaultFeatures.filter(f => f !== "deprecated")
  },
  {
    timeout: 5000,
    retries: 3, 
    features: ["auth", "cache", "deprecated", "logging"]
  }
);
// Result: { timeout: 10000, retries: 5, features: ["auth", "cache", "logging"] }

Array-Specific Function Transformation

Pre-configured defu instance that applies function transformation only to array values in defaults.

/**
 * Defu instance with array-specific function transformation
 * Applies function transformation only when default value is an array
 */  
const defuArrayFn: <Source extends Input, Defaults extends Array<Input | IgnoredInput>>(
  source: Source,
  ...defaults: Defaults
) => Defu<Source, Defaults>;

Usage Examples:

import { defuArrayFn } from "defu";

const config = defuArrayFn(
  {
    plugins: (defaultPlugins) => defaultPlugins.filter(p => p !== "legacy"),
    timeout: () => 30000 // This function won't be called (not an array default)
  },
  {
    plugins: ["auth", "legacy", "cache"], // Array: function will be called
    timeout: 5000 // Number: function ignored, kept as-is
  }
);
// Result: { plugins: ["auth", "cache"], timeout: () => 30000 }

Types

/** Base input object type for defu operations */
type Input = Record<string | number | symbol, any>;

/** Types that are ignored during merging */
type IgnoredInput = 
  | boolean 
  | number 
  | null 
  | any[] 
  | Record<never, any> 
  | undefined;

/** Custom merger function signature */
type Merger = <T extends Input, K extends keyof T>(
  object: T,
  key: keyof T,
  value: T[K],
  namespace: string,
) => any;

/** TypeScript utility for compile-time type merging */
type Defu<S extends Input, D extends Array<Input | IgnoredInput>> = 
  D extends [infer F, ...infer Rest]
    ? F extends Input
      ? Rest extends Array<Input | IgnoredInput>
        ? Defu<MergeObjects<S, F>, Rest>
        : MergeObjects<S, F>
      : F extends IgnoredInput
        ? Rest extends Array<Input | IgnoredInput>
          ? Defu<S, Rest>
          : S
        : S
    : S;

/** Internal type for merging two object types */
type MergeObjects<Destination extends Input, Defaults extends Input> = 
  Destination extends Defaults
    ? Destination
    : Omit<Destination, keyof Destination & keyof Defaults> &
        Omit<Defaults, keyof Destination & keyof Defaults> & {
          -readonly [Key in keyof Destination & keyof Defaults]: 
            Destination[Key] extends null | undefined | void
              ? Defaults[Key] extends null | undefined | void
                ? null | undefined | void
                : Defaults[Key]
              : Defaults[Key] extends null | undefined | void
                ? Destination[Key]
                : Merge<Destination[Key], Defaults[Key]>;
        };

/** Internal type for merging array types */
type MergeArrays<Destination, Source> = 
  Destination extends Array<infer DestinationType>
    ? Source extends Array<infer SourceType>
      ? Array<DestinationType | SourceType>
      : Source | Array<DestinationType>
    : Source | Destination;

/** Internal comprehensive type merger with all defu rules */
type Merge<Destination extends Input, Defaults extends Input> =
  Destination extends null | undefined | void
    ? Defaults extends null | undefined | void
      ? null | undefined | void
      : Defaults
    : Defaults extends null | undefined | void
      ? Destination
      : Destination extends Array<any>
        ? Defaults extends Array<any>
          ? MergeArrays<Destination, Defaults>
          : Destination | Defaults
        : Destination extends Function
          ? Destination | Defaults
          : Destination extends RegExp
            ? Destination | Defaults
            : Destination extends Promise<any>
              ? Destination | Defaults
              : Defaults extends Function
                ? Destination | Defaults
                : Defaults extends RegExp
                  ? Destination | Defaults
                  : Defaults extends Promise<any>
                    ? Destination | Defaults
                    : Destination extends Input
                      ? Defaults extends Input
                        ? MergeObjects<Destination, Defaults>
                        : Destination | Defaults
                      : Destination | Defaults;