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.

4
  • Your code doesn't work. Please provide a working example. Commented May 10, 2017 at 14:17
  • 1
    Shouldn't f (callback: {params: '789'}) be f ({callback: {params: '789'}})? Commented May 10, 2017 at 14:18
  • Have you looked at stackoverflow.com/questions/26578167/…? Commented May 10, 2017 at 14:21
  • Did you mean params = "123"? Commented May 10, 2017 at 17:44

4 Answers 4

10

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'}});

Sign up to request clarification or add additional context in comments.

4 Comments

You can't call your function as f() and callback is not an object.
@totalamd: that's easy to fix though: just add a default value (empty object) for the whole parameter.
If I try to use callback, it's Uncaught ReferenceError: callback is not defined. It's crucial because I want to use several nested objects with same fields name.
@TotalAMD See my answer below for a solution to that :)
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'}}})

2 Comments

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.
@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.
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" } });

4 Comments

f (callback: {name, params: '789'}) is a syntax error.
Yes, I fixed it.
Badacadabra's answer is better (because it's simpler).
Your code seems to work in your snippet, but when I add it, I get an error: ReferenceError: "assignment to undeclared variable param" And my linter complains: ibb.co/ZMw3rtf Why is that and how could I fix it?
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.