Skip to content

Side effects when using the spread operator to merge objects #1017

Closed
@serkodev

Description

@serkodev

Currently, if we use the spread operator (...) to merge objects as shown below, it causes side effects and cannot be tree-shaken:

const ParentSchema = v.object({
  foo: v.string(),
});

const LoginSchema = v.object({
  ...ParentSchema.entries, // <- side effect
  email: v.pipe(v.string(), v.email()),
  password: v.pipe(v.string(), v.minLength(8)),
});

Playground
https://stackblitz.com/edit/node-gq8gcqea?file=index.js

The reason is that bundlers like Rollup set propertyReadSideEffects to true by default, which means the spread operator is treated as a side effect.

We can set the treeshake.preset to "smallest" to disable propertyReadSideEffects and make it tree-shakeable but this may cause other issues.

Solution

I wonder if we could have a v.merge function with a @__NO_SIDE_EFFECTS__ annotation for merging objects, making it possible for bundlers to tree-shake it:

// @__NO_SIDE_EFFECTS__
function merge(a, b) {
  return v.object({
    ...a.entries,
    ...b.entries,
  });
}

Usage

const ParentSchema = v.object({
  foo: v.string(),
});

const LoginSchema = v.merge(
  ParentSchema,
  v.object({
    email: v.pipe(v.string(), v.email()),
    password: v.pipe(v.string(), v.minLength(8)),
  })
);

It would be even better if the v.merge function supported an unlimited number of arguments to merge multiple objects at once.

Alternative: Supports input array in v.object

Also, I am wondering if we can support input an array of entries in v.object, like:

// @__NO_SIDE_EFFECTS__
function object(entries, message) {
  if (Array.isArray(entries)) {
    return v.object(
      entries.reduce((acc, current) => ({ ...acc, ...current }), {}),
      message
    );
  }
  // ... original implementation
}

Usage

const ParentSchema = v.object({
  foo: v.string(),
});

const LoginSchema = v.object([
  ParentSchema.entries,
  {
    email: v.pipe(v.string(), v.email()),
    password: v.pipe(v.string(), v.minLength(8)),
  }
]);

It can also be tree-shaken as the spread operator is not used.


Thanks @antfu for helping clarify this issue.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions