How to handle nested default parameters with object destructuring?
default-parameters, destructuring, ecmascript-6, function, javascript
Solution
The generic pattern for destructuring object properties is
{ … , propertyName: target = defaultInitialiser, … }
(when the property name is exactly the same as the target variable identifier we can join them).
But `target` is not only for variables, it can be any assignment target - including nested destructuring expressions. So for your case (3) you want to use exactly the same approach as with (1) on the top level of the parameter - default initialise the property with an empty object and destructure its parts:
function fn3({foo = 'Foo', bar: {quux = 'Quux', corge = 'Corge'} = {}} = {}) {
console.log(foo, quux, corge);
}
Notice that there is no `bar` variable when you destructure that property. If you want to introduce a `bar` variable for the property as well, you could repeat the property name and do
function fn3({foo = 'Foo', bar, bar: {quux = 'Quux', corge = 'Corge'} = {}} = {}) {
console.log(foo, bar, quux, corge);
}
Problem
I am trying to figure out if it is possible to handle multiple levels of default parameters with destructuring. Since it is not easy to explain with words, here is a step-by-step example... 1 - Flat object destructuring with default parameters Destructuring this object is easy: ``` let obj = { foo: 'Foo', bar: 'Bar' }; ``` With `{foo = 'Foo', bar = 'Bar'} = {}` in a function signature, an object will be created if there is no argument passed when the function is called. If an object is passed but some referenced properties are `undefined`, they will be replaced by their default values. This code works fine: ``` function fn1({foo = 'Foo', bar = 'Bar'} = {}) { console.log(foo, bar); } // OK fn1(); // Foo Bar fn1({foo: 'Quux'}); // Quux Bar fn1({bar: 'Quux'}); // Foo Quux fn1({foo: 'Quux', bar: 'Quux'}); // Quux Quux ``` 2 - Nested object destructuring with shallow default parameters Destructuring this object is harder: ``` let obj = { foo: 'Foo', bar: { quux: 'Quux', corge: 'Corge' } }; ``` `{foo = 'Foo', bar = 'Bar'} = {}` is not a viable option anymore, but now we can use `{foo = 'Foo', bar = {quux: 'Quux', corge: 'Corge'}} = {}` in a function signature. Again, if no argument is given when the function is called, an object is created and the core properties (`foo` and `bar`) are extracted. If an object is passed, only undefined properties (`foo` or `bar`) will be destructured with their default values. The problem is that the object properties of `bar` (`quux` and `corge`) are not part of the "top-level destructuring". This means `quux` or `corge` will be `undefined` if they are not explicitly set when `bar` is passed as an argument: ``` function fn2({foo = 'Foo', bar = {quux: 'Quux', corge: 'Corge'}} = {}) { console.log(foo, bar.quux, bar.corge); } // OK fn2(); // Foo Quux Corge fn2({foo: 'Quux'}); // Quux Quux Corge // Oops! fn2({bar: {quux: 'Baz'}}); // Foo Baz undefined fn2({foo: 'Quux', bar: {corge: 'Baz'}}); // Quux undefined Baz ``` 3 - Nested object destructuring with deep default parameters I would like to set default parameters at all levels of the object hierarchy to use a sort of "cascading destructuring". I tried this, but it does not work: ``` function fn3({foo = 'Foo', bar = ({quux = 'Quux', corge = 'Corge'} = {})} = {}) { console.log(foo, bar.quux, bar.corge); } // Oops! fn3(); // Foo undefined undefined fn3({foo: 'Quux'}); // Quux undefined undefined fn3({bar: {quux: 'Baz'}}); // Foo Baz undefined fn3({foo: 'Quux', bar: {corge: 'Baz'}}); // Quux undefined Baz ``` Do you know if such a feature is allowed in ES6. If yes, how can I implement it?