npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

merge-partially

v2.0.2

Published

A convenience method for overwriting only the values you want

Downloads

17,273

Readme

Coverage Status

merge-partially

mergePartially is a convenience method for overwriting only the values you want

Design Goals

  1. the resulting object will always be the same type/interface as the seed object
  2. it will always be “Typescript first” so you know the type definitions will not differ at runtime (like many of this library's competitors)
  3. all PRs should allow consumers of the library to feel confident to use this library in production and bullet-proof testing scenarios. High code-coverage percentages gaurantee this.

Why would you want to use this:

tl;dr: with mergePartially helps you fold objects together without overwriting the originals. You can have less brittle tests but with all the flexibility you need.

There are many use cases, but I find this function to be most useful in testing scenarios.

Context: Often times when creating a factory function for tests, you want to be able to create a function that

interface IUser {
  id: number;
  firstName: string;
  lastName: string;
  age: number;
}

function makeFakeUser(): IUser {
  return {
    id: 1,
    age: 42,
    firstName: 'John',
    lastName: 'Smith',
  };
}

But what happens when unit test #2 needs the firstName value to be different? If you change the hard-coded value inside of makeFakeUser, then you break unit test #1. So if you don't proceed carefully, then makeFakeUser is at risk of creating brittle tests!

A more flexible approach is provide default values and allow the user to provide their own values.

First, let's try to write the flexible factory function without mergePartially

Ugh this is gonna be long...

function makeFakeUser(overrides?: Partial<IUser>): IUser {
  const defaults = {
    id: 1,
    age: 42,
    firstName: 'John',
    lastName: 'Smith',
  };

  const result = {
    id: overrides && overrides.id !== undefined ? overrides.id : defaults.id,
    age: overrides && overrides.age !== undefined ? overrides.age : defaults.age,
    firstName: overrides && overrides.firstName !== undefined ? overrides.firstName : defaults.firstName,
    lastName: overrides && overrides.lastName !== undefined ? overrides.lastName : defaults.lastName,
  };

  return result;
}

Now let's refactor using mergePartially

Wow look how much fewer lines and characters we have to write to accomplish the same thing:

import { mergePartially, NestedPartial } from 'merge-partially';

function makeFakeUser(overrides?: NestedPartial<IUser>): IUser {
  return mergePartially.deep(
    {
      id: 1,
      age: 42,
      firstName: 'John',
      lastName: 'Smith',
    },
    overrides
  );
}

Examples

See the unit tests for various examples.

F.A.Q. / Troubleshooting

Why wouldn't I just use Object.assign or the spread operator?

These two functions have different goals. Object.assign can merge two different types into a combination type. mergePartially always returns the same type as the seed object. That's one of many reasons why mergePartially is safer than Object.assign.

I see lots of TypeScript stuff. Can I use this in JavaScript too?

Yes. Even though the examples are in TypeScript (since it helps to illustrate the problem that mergePartially solves), you can just remove the type annotations when using mergePartially.

What's the difference between .deep and .shallow?

  • The main difference is that .deep allows you to pass multiple levels of partially supplied objects but .shallow only allows partial objects at the first level.
    • On a more technical level, .deep allows you to pass in NestedPartial<T> as where .shallow only accepts Partial<T>
  • Both will always return the full object

For example:

interface ISeed {
  a: {
    b: {
      c: string;
      d: string;
    };
  };
}

const seed: ISeed = {
  a: {
    b: {
      c: 'c',
      d: 'd',
    },
  },
};

const deepResult = mergePartially.deep(seed, { a: { b: { d: 'new d' } } });
const shallowResult = mergePartially.shallow(seed, {
  a: {
    b: {
      c: 'I had to supply a value for c here but I did not have to supply it in .deep',
      d: 'new d',
    },
  },
});

Why is .shallow even necessary?

There are some data types that are "less-compatible" with the library and therefore require a workaround (click here for the description). It should be rare that you need to use .shallow, but you might prefer .shallow over .deep anyway for explicitness.

Why am I getting some error about never?

If you're seeing a Typescript error similar to "Type 'number' is not assignable to type 'never'" then you it's likely a case where you need to inform the function what the type is for your seed parameter. See this thread for a detailed answer and a fun description in how TypeScript works.

Why is my return type some strange error string?

In order to meet the design goals (see above), mergePartially proactively prevents certain data combinations. See this link for more information on the soluton: https://github.com/dgreene1/merge-partially/blob/master/whyShallowInstead.md

Contributions

PRs are welcome. To contribute, please either make a Github issue or find one you'd like to work on, then fork the repo to make the change.