Function resolveRefs

  • Resolve references within objects and arrays using advanced reference resolution.

    This powerful utility enables dynamic object composition through multiple reference types: global references, relative references, string interpolation, and template inheritance. Perfect for configuration management, dynamic content generation, and object templating.

    Type Parameters

    • T = any

    Parameters

    • object: any

      The source object or array containing references to resolve. References use $path/to/value syntax for global lookups or $./path for relative references.

    • schema: Record<string, any> = ...

      Schema object used as the base for global reference lookups. If not provided, uses a deep clone of the source object. This is where $path/to/value references are resolved against.

    • rules: ResolveRefsRules = {}

      Custom rule definitions for advanced reference processing. Each rule maps a reference key to a tuple [taskName, ...args]. Built-in tasks: 'iterate', 'join', 'ignore', 'if'.

    • extraTasks: ResolveRefsTasks = {}

      Additional custom task functions that can be used in rules. Each task receives the resolved arguments and should return a value.

    Returns T

    A new object with all references resolved and templates expanded.

    Reference values anywhere in the schema using absolute paths:

    • "$path/to/value" - Direct reference to a value
    • "${path/to/value}" - String interpolation (embeds value in string)
    • "prefix-${path/to/value}-suffix" - Mixed string with interpolated values

    Extend and modify referenced objects:

    • { ref: "$path/to/template", newProp: "value" } - Template inheritance
    • { ref: "path/to/template", existingProp: "override" } - Property override

    Reference values within the current object context:

    • "$./property" - Direct relative reference
    • "${./nested/property}" - Relative string interpolation
    • "${./path}-${./other}" - Multiple relative references in strings

    Use the special "." key for references resolved after template merging:

    • "." object contains references processed after base template + overrides
    • Enables templates to reference their final merged state
    • Supports recursive resolution until stable state is reached

    Define reusable logic for complex transformations:

    const rules = {
    "$users": ["iterate", "$data/users", "currentUser"],
    "$joinNames": ["join", "$people", ", ", "$extraPeople"]
    };

    const extraTasks = {
    uppercase: (value: string) => value.toUpperCase(),
    calculate: (a: number, b: number) => a + b
    };
    • iterate(array, itemName) - Process array items with template
    • join(array, separator, ...extra) - Join arrays with separator
    • ignore(path, fallback) - Safe lookup with fallback
    • if(condition, trueValue, falseValue) - Conditional resolution

    Basic global references:

    const config = { api: { url: "https://api.com", key: "abc123" } };
    const settings = { endpoint: "$api/url", auth: "Bearer ${api/key}" };

    const result = resolveRefs(settings, config);
    // { endpoint: "https://api.com", auth: "Bearer abc123" }

    Template inheritance with property overrides:

    const data = {
    defaultUser: { name: "Guest", role: "viewer", active: false },
    admin: { ref: "$defaultUser", name: "Admin", role: "admin", active: true },
    viewer: { ref: "$defaultUser", name: "John" }
    };

    const result = resolveRefs(data);
    // result.admin = { name: "Admin", role: "admin", active: true }
    // result.viewer = { name: "John", role: "viewer", active: false }

    Relative references with template system:

    const data = {
    user1: { ref: "$templates/user", firstName: "Alice", lastName: "Smith" },
    user2: { ref: "$templates/user", firstName: "Bob", lastName: "Jones" },
    templates: {
    user: {
    firstName: "",
    lastName: "",
    email: "",
    ".": {
    fullName: "${./firstName} ${./lastName}",
    email: "${./firstName}.${./lastName}@company.com",
    profile: {
    displayName: "$./fullName",
    username: "$./firstName"
    }
    }
    }
    }
    };

    const result = resolveRefs(data);
    // result.user1.fullName = "Alice Smith"
    // result.user1.email = "Alice.Smith@company.com"
    // result.user1.profile.displayName = "Alice Smith"

    Configuration management with mixed references:

    const config = {
    env: { baseUrl: "https://api.prod.com", version: "v2" },
    services: {
    auth: { ref: "$templates/service", path: "/auth", timeout: 5000 },
    users: { ref: "$templates/service", path: "/users", timeout: 3000 }
    },
    templates: {
    service: {
    path: "/",
    timeout: 30000,
    retries: 3,
    ".": {
    url: "${env/baseUrl}/${env/version}${./path}",
    config: {
    endpoint: "$./url",
    timeout: "$./timeout",
    retries: "$./retries"
    }
    }
    }
    }
    };

    const result = resolveRefs(config);
    // result.services.auth.url = "https://api.prod.com/v2/auth"
    // result.services.auth.config.endpoint = "https://api.prod.com/v2/auth"

    Custom rules and tasks:

    const data = { users: ["Alice", "Bob"], items: [1, 2, 3] };
    const template = {
    userList: "$buildUserList",
    summary: "$joinItems"
    };

    const rules = {
    "$buildUserList": ["iterate", "$users", "user"],
    "$joinItems": ["join", "$items", " + "]
    };

    const extraTasks = {
    format: (template: string, ...values: any[]) =>
    template.replace(/{(\d+)}/g, (_, i) => values[i])
    };

    const result = resolveRefs(template, data, rules, extraTasks);
    1. Schema Organization: Keep templates in a dedicated section (e.g., templates/)
    2. Reference Clarity: Use descriptive paths like config/database/url vs c/d/u
    3. Template Design: Use relative references in "." for dynamic template properties
    4. Performance: Avoid deep circular references that could cause infinite loops
    5. Type Safety: Consider using TypeScript interfaces for your schema structure
    • Invalid references return the original reference string
    • Missing paths in schema return undefined
    • Circular references are prevented through iteration limits
    • Type mismatches in string interpolation are converted to strings