Null-safe property access (and conditional assignment) in ES6/2015
JavascriptCoffeescriptEcmascript 6BabeljsJavascript 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:
- Optional Chaining is in the specification now (ES2020) and supported by all modern browsers, more in the archived proposal: https://github.com/tc39/proposal-optional-chaining
- babel-preset-env: If you need to support older environments that don't have it, this is probably what you want https://babeljs.io/docs/en/babel-preset-env
- Babel v7 Plugin: https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining
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.
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
?.
and ??
2020 Solution, 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%
--
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%
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