Berkin
Berkin

Reputation: 1664

How to spread an object as individual arguments in a function whose declaration I cannot change?

I have an object with some properties such as;

integrationConfig = {
  iconEmoji: ':myIconEmoji:', 
  team: 'myTeam', 
  text: 'myText', 
  channel: 'myChannel', 
  botName: 'myBot'
}

I am passing this object to a function below as shown (attachments is not important).

return await this.pushToSlack(...integrationConfig, attachments);

Importantly, this function is part of an NPM Package, so I don’t want to change the function declaration.

The function is declared like this:

exports.pushToSlack = function (channel, text, botName, iconEmoji, team, attachments, cb = function () {}) {
  // […]
}

I put some breakpoint to the pushToSlack function but the debugger didn’t jump into that line. I guess the function is not called somehow. I also receive this error:

Debug: internal, implementation, error 
    TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
    at Function.all (<anonymous>)

Have you got any idea?

Upvotes: 2

Views: 2035

Answers (3)

vdj4y
vdj4y

Reputation: 2669

so you have function

const pushToSlack = function (channel, text, botName, iconEmoji, team, attachments, cb = function () {}) {

this function accepts 7 arguments.

You have good idea to spread an object integrationConfig, hoping that it will become 7 arguments.

However spreading an object will just clone the exact same object with exact same property.

const obj = {...integrationConfig} is equal to ONE Object.

You can instead pass an array and spread it.

pushToSlack(...Object.values(integrationConfig)) But an object does not guarantee the order of the key. so it could be

// order is not guaranteed!!!
...Object.values(integrationConfig) === [botName, channel, iconEmoji, team, text] 
// or
...Object.values(integrationConfig) === [channel, botName, iconEmoji, team, text] 
// or 
...Object.values(integrationConfig) === [team, botName, iconEmoji, channel, text] 

However your function need a fix order of arguments.

  1. channel as first argument,
  2. text as second argument
  3. etc.

I believe you can do like this

const integrationConfig = {
        iconEmoji: ':myIconEmoji:', 
        team: 'myTeam', 
        text: 'myText', 
        channel: 'myChannel', 
        botName: 'myBot'
    }

const {iconEmoji, team, text, channel, botName} = integrationConfig

pushToSlack(channel, text, botName, iconEmoji, team, etc);

Upvotes: 2

Mister Jojo
Mister Jojo

Reputation: 22320

Spread syntax is not usable for that
use Destructuring assignment

integrationConfig = 
  { iconEmoji : ':myIconEmoji:'
  , team      : 'myTeam'
  , text      : 'myText'
  , channel   : 'myChannel'
  , botName   : 'myBot'
  } 

the call :

return await this.pushToSlack( integrationConfig, attachments);

the function :

 exports.pushToSlack = function ({channel, text, botName, iconEmoji, team}, attachments, ...
 //..Destructuring assignment....^.......................................^
 // Arguments can be in any order you want
 // and no obligation to have all of them

Upvotes: 1

Sebastian Simon
Sebastian Simon

Reputation: 19485

If you cannot change the parameter list of the function, you’ll have to define the order the arguments are expected in, then map your object onto this order:

const argumentOrder = [
    "channel",
    "text",
    "botName",
    "iconEmoji",
    "team"
  ];

// […]

return await this.pushToSlack(...argumentOrder.map((property) => integrationConfig[property]), attachments);

The error you’re getting means that func(...integrationConfig) won’t work. Yes, the function is never called. There’s a distinction between object spread and iterable spread. Arguments and arrays use iterable spread, which means that two conditions must be met: firstly, the value you want to spread must be non-nullish; and secondly, the value must be iterable, i.e. something that has Symbol.iterator. Object spread only checks the first condition.

You could, theoretically, add such a symbol property into your object, which would allow you to use your original syntax:

const integrationConfig = {
    iconEmoji: ":myIconEmoji:",
    team: "myTeam",
    text: "myText",
    channel: "myChannel",
    botName: "myBot",
    *[Symbol.iterator](){
      yield this.channel;
      yield this.text;
      yield this.botName;
      yield this.iconEmoji;
      yield this.team;
    }
  };

// […]

return await this.pushToSlack(...integrationConfig, attachments);

Upvotes: 3

Related Questions