Advanced Static Types in TypeScript

This course explores the capabilities of TypeScript’s type system and shows how to use advanced static types in practice.

Watch the Course

Combining Settings Objects with Lodash: _.assign or _.merge?

When you're developing a JavaScript component which lets the user provide an object holding some options, you usually need to merge its values with your component's defaults. Settings for which the user didn't provide a value should fall back to a reasonable default.

Suppose we have the following default and user settings:

// Defined within your component
var defaultSettings = {
    strictMode: true,
    formatting: {
        finalNewline: true,
        quotes: "double"
    }
};

// Provided by the developer using your component
var userSettings = {
    formatting: {
        quotes: "single"
    }
};

Note that no value for the strictMode property was specified by the user. That's perfectly fine, we're just going to use the default value of true. The same goes for the finalNewline property, we're falling back to true as well. Only the quotes value changed from its default value of "double" to "single".

In the end, we'd like the merged settings to look like this:

var settings = {
    strictMode: true,
    formatting: {
        finalNewline: true,
        quotes: "single"
    }
};

The lodash library contains two similar functions, _.assign and _.merge, that assign property values of some source object(s) to a target object, effectively merging their properties. Let's take a look at their differences.

Merging Properties Using _.assign

The _.assign function (also called _.extend) accepts as its first parameter a target object to which all properties of the following parameters will be assigned. Values defined by later parameters will overwrite sooner values.

A common pattern is to provide {}, an empty object literal, as the target into which all settings are merged. The default values come first, followed by user-provided settings which overwrite already defined properties.

That sounds perfect for merging settings with default fallback values, does't it? Well, not entirely, as this example shows:

var assignedSettings = _.assign({}, defaultSettings, userSettings);

// {
//     strictMode: true,
//     formatting: {
//         quotes: "single"
//     }
// }

As you can see, the formatting property of the user's settings was assigned as a whole, as the _.assign. name already suggests. The true value of finalNewline, another property of the formatting object, is now lost.

Let's compare this with the behavior of the _.merge function.

Merging Properties Using _.merge

The _.merge function has the same signature as _.assign, but behaves a little differently. Instead of assigning values as a whole, it recursively merges properties that don't hold the value undefined into the target object.

Let's combine the user settings with the default values once more:

var mergedSettings = _.merge({}, defaultSettings, userSettings);

// {
//     strictMode: true,
//     formatting: {
//         finalNewline: true,
//         quotes: "single"
//     }
// }

Much better! Now, the formatting object hasn't been replaced by the partial user definition, but has had its default values merged with the user settings.

So, if you intend to perform a deep merge of user-provided settings with your own default values, use _.merge rather than _.assign.

Learn Node