Null-coalescing default values in destructuring

# Cyril Auburtin (5 years ago)

Similarly to

const { x = 'a' } = {}; // z === 'a'
const { y = 'a' } = { y: undefined }; // z === 'a'
const { z = 'a' } = { z: null }; // z === null

I'd like to propose

const { x =? 'a' } = {}; // z === 'a'
const { y =? 'a' } = { y: undefined }; // z === 'a'
const { z =? 'a' } = { z: null }; // z === 'a'

Which would handle also null values in default cases

This is because default destructuring introduced in ES6 doesn't handle null values, null values in JSON are quite common from APIs, it'd be convenient

It's also inspired by the null-coalescing operator tc39/proposal-nullish-coalescing

# guest271314 (5 years ago)

Given input valid JSON JSON.parse() or JSON.stringify() replacer function can be utilized to substitute "undefined" for null.

const convertJSONNullToUndefined = json =>
  Object.assign({}
  // if input is valid JSON string and not a JavaScript plain object omit JSON.stringify(), use JSON.parse() with same replacer function
  , ...Object.entries(JSON.parse(JSON.stringify(json, (key, value) => value === null ? `${void 0}` : value)))
    .map(([key, value]) => value === `${void 0}` ? {[key]: void value} : value)); // {x: undefined, y: undefined, z: undefined}
const {z = "a"} = convertJSONNullToUndefined({ x: null, y: 123, z: null }); // "a" assigned to z

Alternatively RegExp can be used to replace JSON string values

const convertJSONNullToUndefined = json =>
  Object.assign({},
  ...function*() {
    // use negative lookahead to match ":" followed by null followed by comma or closing curly bracket character
    // replace null with "undefined"
    const o = JSON.parse(JSON.stringify(json).replace(/(?![:])null(?=[,}])/g, `"${void 0}"`));
    // set value to undefined
    for (const key in o) yield {[key]: o[key] === `"${void 0}"` ? void o[key] : o[key]}
  }());
const {z = "a"} = convertJSONNullToUndefined({ x: null, y: 123, z: null }); // "a" assigned to z
# Oliver Dunk (5 years ago)

Given input valid JSON JSON.parse() or JSON.stringify() replacer function can be utilized to substitute "undefined" for null.

guest271314, that’s a lot of code! It feels like if anything, your example backs up the proposal.

const { z =? 'a' } = { z: null };

I’m not sure I like the proposed syntax. The question mark indicates to me that it’s the value on the lhs that’s optional - when in reality, it’s the other way around.

# guest271314 (5 years ago)

The bytes used for code can be reduced. JSON.stringify() or JSON.parse() and replace() could be used. The original proposal mentions JSON, though includes the case of

const { y =? 'a' } = { y: undefined }; // z === 'a'

which is not valid JSON but a JavaScript plain object. The brief example handles JSON and/or JavaScript plain object input.

Is the actual input a JSON string?

There would probably at least that amount of code to implement the extension to the = operator in JavaScript.

# guest271314 (5 years ago)

Slightly less code (still un-golfed as that does not appear to be the stated requirement described at OP) using the same/similar pattern.

let input_json_string = `{"z":null}`;
const convertJSONNullToUndefined = json => {
  const o = JSON.parse(json);
  for (const key in o) if (o[key] === null) o[key] = void 0;
  return o;
}

convertJSONNullToUndefined(input_json_string); // do destructuring stuff

Alternatively a single use function expecting a single input parameter that is a valid JSON string consisting of a {"key":"value"}

const convertJSONNullToUndefined = (json, o = JSON.parse(json), [k] = Object.keys(o), res = o[k] !== null ? o : {[k]: void 0}) => res;

const { z = 'a' } = convertJSONNullToUndefined(`{"z":null}`);

Since .find() returns undefined if the value is not matched

const [z = 'a'] = [Object.values(JSON.parse(`{"z":null}`)).find(value => value !== null)];

or

const [z = 'a'] = Object.values(JSON.parse(`{"z":null}`)).filter(value => value !== null);