Null-safe property access (and conditional assignment) in ES6/2015

JavascriptCoffeescriptEcmascript 6Babeljs

Javascript Problem Overview


Is there a null-safe property access (null propagation / existence) operator in ES6 (ES2015/JavaScript.next/Harmony) like ?. in CoffeeScript for example? Or is it planned for ES7?

var aThing = getSomething()
...
aThing = possiblyNull?.thing

This will be roughly like:

if (possiblyNull != null) aThing = possiblyNull.thing

Ideally the solution should not assign (even undefined) to aThing if possiblyNull is null

Javascript Solutions


Solution 1 - Javascript

Update (2022-01-13): Seems people are still finding this, here's the current story:

Update (2017-08-01): If you want to use an official plugin, you can try the alpha build of Babel 7 with the new transform. Your mileage may vary

https://www.npmjs.com/package/babel-plugin-transform-optional-chaining

Original:

A feature that accomplishes that is currently in stage 1: Optional Chaining.

https://github.com/tc39/proposal-optional-chaining

If you want to use it today, there is a Babel plugin that accomplishes that.

https://github.com/davidyaha/ecmascript-optionals-proposal

Solution 2 - Javascript

It's not as nice as the ?. operator, but to achieve a similar result you could do:

user && user.address && user.address.postcode

Since null and undefined are both falsy values (see this reference), the property after the && operator is only accessed if the precedent it not null or undefined.

Alternatively, you could write a function like this:

function _try(func, fallbackValue) {
    try {
        var value = func();
        return (value === null || value === undefined) ? fallbackValue : value;
    } catch (e) {
        return fallbackValue;
    }
}

Usage:

_try(() => user.address.postcode) // return postcode or undefined 

Or, with a fallback value:

_try(() => user.address.postcode, "none") // return postcode or a custom string

Solution 3 - Javascript

2020 Solution, ?. and ??

You can now directly use ?. (Optional Chaining) inline to safely test for existence. All modern browsers support it.

?? (Nullish Coalescing) can be used to set a default value if undefined or null.

aThing = possiblyNull ?? aThing
aThing = a?.b?.c ?? possiblyNullFallback ?? aThing

If a property exists, ?. proceeds to the next check, or returns the valid value. Any failure will immediately short-circuit and return undefined.

const example = {a: ["first", {b:3}, false]}

example?.a  // ["first", {b:3}, false]
example?.b  // undefined

example?.a?.[0]     // "first"
example?.a?.[1]?.a  // undefined
example?.a?.[1]?.b  // 3

domElement?.parentElement?.children?.[3]?.nextElementSibling

null?.()                // undefined
validFunction?.()       // result
(() => {return 1})?.()  // 1

To ensure a default defined value, you can use ??. If you require the first truthy value, you can use ||.

example?.c ?? "c"  // "c"
example?.c || "c"  // "c"

example?.a?.[2] ?? 2  // false
example?.a?.[2] || 2  // 2

If you do not check a case, the left-side property must exist. If not, it will throw an exception.

example?.First         // undefined
example?.First.Second  // Uncaught TypeError: Cannot read property 'Second' of undefined

?. Browser Support - 92%, Nov 2021

?? Browser Support - 92%

Mozilla Documentation

--

Logical nullish assignment, 2020+ solution

New operators are currently being added to the browsers, ??=, ||= and &&=. They don't do quite what you are looking for, but could lead to same result depending on the aim of your code.

> NOTE: These are not common in public browser versions yet, but Babel should transpile well. Will update as availability changes.

??= checks if left side is undefined or null, short-circuiting if already defined. If not, the left side is assigned the right-side value. ||= and &&= are similar, but based on the || and && operators.

Basic Examples
let a          // undefined
let b = null
let c = false

a ??= true  // true
b ??= true  // true
c ??= true  // false
Object/Array Examples
let x = ["foo"]
let y = { foo: "fizz" }

x[0] ??= "bar"  // "foo"
x[1] ??= "bar"  // "bar"

y.foo ??= "buzz"  // "fizz"
y.bar ??= "buzz"  // "buzz"

x  // Array [ "foo", "bar" ]
y  // Object { foo: "fizz", bar: "buzz" }

Browser Support Nov 2021 - 90%

Mozilla Documentation

Solution 4 - Javascript

No. You may use lodash#get or something like that for this in JavaScript.

Solution 5 - Javascript

Vanilla alternative for safe property access

(((a.b || {}).c || {}).d || {}).e

The most concise conditional assignment would probably be this

try { b = a.b.c.d.e } catch(e) {}

Solution 6 - Javascript

No, there is no null propagation operator in ES6. You will have to go with one of the known patterns.

You may be able to use destructuring, though:

({thing: aThing} = possiblyNull);

There are many discussions (e.g. this) to add such an operator in ES7, but none really took off until several years later when optional chaining syntax was standardised in ES2020.

Solution 7 - Javascript

Going by the list here, there is currently no proposal to add safe traversal to Ecmascript. So not only is there no nice way to do this, but it is not going to be added in the forseeable future.

Edit: Since I originally made this post, it was in fact added to the language.

Solution 8 - Javascript

// Typescript
static nullsafe<T, R>(instance: T, func: (T) => R): R {
    return func(instance)
}

// Javascript
function nullsafe(instance, func) {
    return func(instance);
};

// use like this
const instance = getSomething();
let thing = nullsafe(instance, t => t.thing0.thing1.thingx);

Solution 9 - Javascript

A safe deep get method seems like a natural fit for underscore.js but there the issue is avoiding string programming. Modifying @Felipe's answer to avoid string programming (or at least pushes edge cases back to the caller):

function safeGet(obj, props) {
   return (props.length==1) ? obj[keys[0]] :safeGet(obj[props[0]], props.slice(1))
}

Example:

var test = { 
  a: { 
    b: 'b property value',
    c: { }
  } 
}
safeGet(test, ['a', 'b']) 
safeGet(test, "a.b".split('.'))  

Solution 10 - Javascript

I thought this question needed a bit of a refresh for 2018. This can be done nicely without any libraries using Object.defineProperty() and can be used as follows:

myVariable.safeGet('propA.propB.propC');

I consider this safe (and js-ethical) because of the writeable and enumerable definitions now available for the defineProperty method of Object, as documented in MDN

function definition below:

Object.defineProperty(Object.prototype, 'safeGet', { 
    enumerable: false,
    writable: false,
    value: function(p) {
        return p.split('.').reduce((acc, k) => {
            if (acc && k in acc) return acc[k];
            return undefined;
        }, this);
    }
});

I've put together a jsBin with console output to demonstrate this. Note that in the jsBin version I've also added a custom exception for empty values. This is optional, and so I've left it out of the minimal definition above.

Improvements are welcomed

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionᅙᄉᅙView Question on Stackoverflow
Solution 1 - JavascriptbasicdaysView Answer on Stackoverflow
Solution 2 - JavascripttocquevilleView Answer on Stackoverflow
Solution 3 - JavascriptGiboltView Answer on Stackoverflow
Solution 4 - JavascriptGirafaView Answer on Stackoverflow
Solution 5 - JavascriptyaggerView Answer on Stackoverflow
Solution 6 - JavascriptBergiView Answer on Stackoverflow
Solution 7 - JavascriptAntimonyView Answer on Stackoverflow
Solution 8 - JavascriptGary FuView Answer on Stackoverflow
Solution 9 - JavascriptprototypeView Answer on Stackoverflow
Solution 10 - JavascriptFelipeView Answer on Stackoverflow