Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

undefined values are merged #13

Closed
kcwitt opened this issue Jul 19, 2019 · 4 comments · Fixed by #14
Closed

undefined values are merged #13

kcwitt opened this issue Jul 19, 2019 · 4 comments · Fixed by #14

Comments

@kcwitt
Copy link

kcwitt commented Jul 19, 2019

Based on the following code:

    const merge = require('merge-options');
    const result = merge({value: 10}, {value: undefined});

result.value should be '10', but it is 'undefined'.

Whether it really should return 10 is arguable, so my argument is that {} is functionally equivalent to {value: undefined} when getting a value from on object, so as inputs to merge they should both provide equivalent output. Where they are not functionally equivalent is when iterating the objects keys, but when dealing with options (and being mindful if the distinction between undefined and null) the rule of least surprise (for me at least) is to completely ignore undefined values (but still allow null values to supersede values of lower precedence).

The practical application of this is putting environment variables (which may or may not be defined) as the highest precedence item in the merge, but only if it has been defined.
const port = merge(defaults, {port: process.env.PORT});

@schnittstabil
Copy link
Owner

schnittstabil commented Jul 21, 2019

Fair enough, but the readme clearly states:

merge-options considers plain objects as Option Objects, everything else as Option Values.

As undefined is not a plain object, your examples describe the expected behavior.

Please note: the more recent (non-recursive) spread operator works the same way:

const defaults = { port: 443 };
const options = { port: process.env.PORT };

mergeOptions(defaults, options)
// => { port: undefined }

{...defaults, ...options}
// => { port: undefined }

Hence, in your case, I would suggest to make your environment variables more useful:

// index.js
const mergeOptions = require('merge-options');

const envKeys = [ 'PORT' ];
const env = Object.fromEntries(Object.entries(process.env)
    .filter(([k]) => envKeys.includes(k))
    .map(([k, v]) => [k.toLowerCase(), v])
);

console.log('env:', env)
const options = mergeOptions({ port: 443 }, env);
console.log('options:', options);
$ PORT=8080 node index.js
# env: { port: '8080' }
# options: { port: '8080' }

$ node index.js
# env: {}
# options: { port: 443 }

@achingbrain
Copy link
Contributor

Would you accept a PR that added this behaviour as a config option similar to concatArrays called ignoreUndefined or similar?

I run into situations where this would be useful all the time but don't think the world needs Yet Another Options Merging Module.

@schnittstabil
Copy link
Owner

schnittstabil commented Oct 19, 2019

👍 @achingbrain I don't see a problem with an option – ignoreUndefined sounds good to me.

@schnittstabil
Copy link
Owner

Thanks to @achingbrain (merge-options@^2.0.0):

const mergeOptions = require('merge-options').bind({ignoreUndefined: true});

const result = mergeOptions({port: 443}, {port: process.env.PORT});
console.log(result);
$ PORT=8080 node index.js 
# { port: '8080' }

$ node index.js 
# { port: 443 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants