9

I want to have a function with default parameters inside nested objects, and I want to be able to call it either f() or specifying only individual parameters.

// A function with nested objects with default parameters:

function f({ a = 1, callback = ({ name, param } = { name: "qwe", param: 123 }) } = {}) {
    console.log("a:", a);
    console.log("callback:", callback);
}

// And I want to run it like this:

f();
f({ callback: { params: "456" } });

// But 'callback.name' becomes undefined.
TotalAMD
  • 887
  • 1
  • 10
  • 20

4 Answers4

9

When destructuring is mixed with default parameters, I admit the code is hard to read and write (especially when there are nested objects...).

But I think you are trying to do that:

function f({callback: {name = "cbFunction", params = "123"} = {}} = {}) {
  console.log(name);
  console.log(params);
}

f();
f({callback: {params: '789'}});
TotalAMD
  • 887
  • 1
  • 10
  • 20
Badacadabra
  • 8,043
  • 7
  • 28
  • 49
3

I found none of the answers here to be what he wanted. But it IS actually possible in a somewhat sexy way by doing this:

(EDIT: Simplified syntax and also show how to add default values for subobjects)

function f({
    a = 1,
    callback = {}
  } = {}) {
  callback = { // default values
    name: "cbFunction",
    params: "123",
    ...callback // overwrites it with given values
  }
  // do the same for any subobjects
  callback.subObject = {
    arg1: 'hi',
    arg2: 'hello',
    ...callback.subObject
  }
  console.log("a:", a)
  console.log("callback:", callback)
}

f()
f({a: 2, callback: {params: '789', subObject: {arg2: 'goodbye'}}})
  • Your way works Joakim, and you are right that none of the other answers fully meets the requirements in the question. However, the approach from @Badacadabra will actually work as desired, but is missing the top-level object property required in the original question. Can't post multi-line code here so will make another answer. – Tim Stewart Feb 03 '20 at 04:43
  • @TimStewart TotalAMD also said in a comment that "I want to use several nested objects with same fields name". So if he tries that approach with callback1 and callback2 as arguments then he would have to use different field names in them. – Joakim L. Christiansen Feb 03 '20 at 12:23
1

Turned out to call it like this solves the problem, but is it the best way?

function f({
  a = 1,
  callback = ({
    name,
    param
  } = {
    name: "qwe",
    param: 123
  })
} = {}) {
  console.log("a:", a);
  console.log("callback:", callback);
}

f();
f({ callback: { name, params: "456" } });
TotalAMD
  • 887
  • 1
  • 10
  • 20
0

Answer by @Badacadabra is nearly correct but missing the other top level parameter specified in the question.

function f({a = 1, callback: {name = "qwe", params = "123"} = {}} = {}) {
  console.log(a);
  console.log(name);
  console.log(params);
}

However note that within the function body the properties of callback are addressed without the containing object. You could reconstitute them into such an object if you wanted with the line:

const callback = { name, params }

Either way, from the invocation point this works to fill in all missing values from all levels such as:

f({a: 2})

f({a: 2, callback: { name: "abc"}})

f({a: 2, callback: { params: "456" }})

etc.

EDIT

In response to Joakim's comment:

TotalAMD also said in a comment that "I want to use several nested objects with same fields name". So if he tries that approach with callback1 and callback2 as arguments then he would have to use different field names in them.

I missed that original requirement. One way to maintain the desired, duplicated nested names within the function interface would be to alias them within the scope of the function, as follows:

function f({
    a = 1, 
    callback1: {name: name1 = "abc", params: params1 = "123"} = {},
    callback2: {name: name2 = "def", params: params2 = "456"} = {},
} = {}) {
  console.log(a);
  console.log(name1);
  console.log(params1);
  console.log(name2);
  console.log(params2);
}

You can then call the function with the designed interface and expected results:

f ({ callback1: { name: "One" }, callback2: { name: "Two" } })

Caveat: Whilst technically possible and potentially useful, this could get messy at deeper nesting levels. It might then be worth looking for an alternative interface design with less indirection.

Tim Stewart
  • 759
  • 6
  • 8