Any chance for an `Object.assignProperties` ?

# Andrea Giammarchi (2 months ago)

Both Object.assign and {...extend} suffer a tiny gotcha: properties are never assigned, neither retrieved, as accessors, with side-effects too.

Example:

const Counter = {
  _count: 0,
  get count() {
    return this._count++;
  }
};

const incr1 = Object.assign({}, Counter);
const incr2 = {...Counter};

console.log(
  incr1.count,    // 0
  incr2.count,    // 1
  Counter._count  // 2
);

// functionality also compromised
console.log(incr1.count === incr1.count);

Not only most of the time this is unexpected, but there's literally no way to pass along accessors with a similar Object.assign ease, even if that's what most developers would expect (at least up to the first time they encounter above issue).

How about we introduce Object.assignProperties instead?

A polyfill example:

const {assign, defineProperties, getOwnPropertyDescriptors} = Object;
const assignProperties = (base, ...mixins) => defineProperties(
  base,
  mixins.reduce(
    (descriptors, mixin) => assign(
      descriptors,
      getOwnPropertyDescriptors(mixin)
    ),
    {}
  )
);

We can now use objects and mixins without side-effecting sources used to extend, and preserving accessors in the process.

const Counter = {
  _count: 0,
  get count() {
    return this._count++;
  }
};

const incr1 = Object.assignProperties({}, Counter);
const incr2 = Object.assignProperties({}, Counter);

console.log(
  incr1.count,    // 0
  incr2.count,    // 0
  Counter._count  // 0
);

// always false: preserved functionality
console.log(incr1.count === incr1.count);

Thoughts ?

# Jordan Harband (2 months ago)

Object.defineProperties(target, Object.getOwnPropertyDescriptors(source))?

# Andrea Giammarchi (2 months ago)

That has nothing to do with this, right?

const {assign, defineProperties, getOwnPropertyDescriptors} = Object;
const assignProperties = (base, ...mixins) => defineProperties(
  base,
  mixins.reduce(
    (descriptors, mixin) => assign(
      descriptors,
      getOwnPropertyDescriptors(mixin)
    ),
    {}
  )
);
# Jordan Harband (2 months ago)

It seems like it’s the exact implementation you want, just for 1 object instead of N.

Object.assign was added because versions of it were all over the web, used very frequently. How frequent is the pattern where people want to copy descriptors, such that it would deserve reification in the language?

# Andrea Giammarchi (2 months ago)

The fact assign doesn't copy descriptors and has potential side-effects is documented extensively though, meaning there is room for improvement, or simply a missing native way to do that, like it was for getOwnPropertyDescriptors, that following your logic should've never landed.

But I guess I'd go with the usual "yet another micro library" approach instead, so that at least I won't have to deal with assign shenanigans when I mean to copy accessors too.

# Andrea Giammarchi (2 months ago)

Anyway, if anyone is interested, I've published the assign-properties module [1]

[1] WebReflection/assign-properties#readme