181

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

ᆼᆺᆼ
  • 14,442
  • 8
  • 53
  • 87
  • 4
    @naomik This kind of null checking can be very useful for if statements where you're checking for a deeply nested property, e.g. `if( obj?.nested?.property?.value )` instead of `if( obj && obj.nested && obj.nested.property && obj.nested.property.value )` – Sean Walsh Aug 22 '15 at 19:55
  • @SeanWalsh if your objects are that deeply nested, or if your functions are digging that deeply in your objects, there's probably several other problems with your app as well. – Thank you Aug 22 '15 at 23:49
  • 1
    compare `var appConfig = loadConfig(config, process.env); connect(appConfig.database);` to `connect(config)`. You can pass a much simpler object to `connect` instead of passing the whole `config` object, you can use `conf.username`, `conf.password` instead of attempting something like `config[process.env]?.database?.username`, `config[process.env]?.database?.password`. Reference: [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter). – Thank you Aug 22 '15 at 23:59
  • Also, if you do something like set defaults or sanitize properties (this could be done in `loadConfig` in the example above), you can make assumptions about the existence of properties and skip null checking in countless areas of your app. – Thank you Aug 22 '15 at 23:59
  • 4
    @naomik As long as the language supports nesting objects, it's still a useful feature - regardless of what you or I think of the architecture of the app itself. As an aside, complex object graphs like this are very common in ORMs that are modeling a complex data model. – Sean Walsh Aug 23 '15 at 00:26
  • @naomik, I would have liked to avoid this being turned into a pointless OO-vs-FP holy war, however if you consider the null-safe property access operator more carefully, you will see that it is in fact a "Maybe monad": `possiblyNull` can be Object or null, and if it's null then the rest of the operators (`.`*) are short-circuited and the result is null (or undefined or voidrvalue, but that's detail). – ᆼᆺᆼ Aug 23 '15 at 01:26

10 Answers10

115

Update (2020-01-31): 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

basicdays
  • 1,666
  • 1
  • 14
  • 16
  • Do I understand correctly that that doesn't include conditional assignment though? `street = user.address?.street` would set `street` in any case? – ᆼᆺᆼ Jan 27 '17 at 16:14
  • 1
    Actually unfortunately I think you're right. `Street` _I think_ would be assigned `undefined`. _But_ at least it wouldn't throw on trying to access properties on undefined. – basicdays Jan 31 '17 at 16:08
  • 2
    It also looks like you can you the operator on the left hand, and if it evals anything to undefined, the right hand isn't evaluated. The babel plugin may vary a bit, haven't tested it much myself yet. – basicdays Jan 31 '17 at 16:22
  • 1
    Regarding conditional assignment on the left-hand-side of the `=`, looks like that isn't supported in the official spec currently. https://github.com/tc39/proposal-optional-chaining#not-supported – basicdays Jun 27 '19 at 23:14
  • Fun fact, now moved to stage 3 and will likely be in TypeScript in the next version or two. Will update here when it happens. – basicdays Jul 25 '19 at 20:23
  • 4
    As of May 2020, looks like current browsers and Typescript have implemented this! – Josh Diehl May 04 '20 at 02:55
80

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
tocqueville
  • 4,111
  • 1
  • 34
  • 48
  • Right probably I should clarify the question, the main thing I was after was conditional assignment – ᆼᆺᆼ Jan 09 '17 at 17:36
  • just a corollary to that; to find the inverse safely, you can use `!(user && user.address && user.address.postcode)` :) – rob2d Mar 06 '17 at 19:06
  • `foo && foo.bar && foo.bar.quux ...` in a large codebase this is ugly and adds a lot of complexity that you would be better to avoid. – Skylar Saveland Oct 21 '17 at 20:25
  • This is a very thoughtful answer with concise examples. Thank you for the options. I particularly like that both these approaches offer the ability to assign a default/fallback value. In modern uses of the language, it's a nice convenience to be to work with variables that are guaranteed to have values --that way we don't need to constantly check for `null` and `undefined`. – theUtherSide Apr 04 '18 at 05:03
  • 2
    This is the cleanest solution I've found on the internet. I use this with typescript: `_get(func: () => T, fallbackValue?: T) :T` – tomwassing Aug 27 '18 at 07:52
  • 3
    If `user.address.postcode` is undefined, `_try(() => user.address.postcode, "")` will return `undefined` instead of `""`. So the code `_try(() => user.address.postcode, "").length` will raise an exception. – Alexander Chen Sep 19 '18 at 11:42
  • @AlexanderChen true, I updated the answer to take that into account, thanks – tocqueville Sep 21 '18 at 16:32
  • This is the best solution by far I have come across! – Yini Aug 09 '19 at 02:53
  • `value == null` is shorthand for `value === null || value === undefined` – bkDJ Dec 17 '20 at 09:30
36

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

Girafa
  • 2,302
  • 1
  • 14
  • 30
24

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 - 89%, Mar 2021

?? Browser Support - 89%

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 Mar 2021 - 83%

Mozilla Documentation

Gibolt
  • 24,018
  • 9
  • 129
  • 89
  • 1
    The question is also about conditional assignment – ᆼᆺᆼ Jul 10 '20 at 08:41
  • Added a few examples with `?.` and `??`, and a detailed upcoming solution that may work for your situation. Best current solution is probably to just do `aThing = possiblyNull?.thing ?? aThing` – Gibolt Jul 10 '20 at 16:42
19

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) {}
yagger
  • 2,765
  • 1
  • 13
  • 18
  • So how would you write the assignment in the question using this mechanism? Don't you still need a conditional to not assign in case the `possiblyNull` is not defined/`null`? – ᆼᆺᆼ Nov 15 '16 at 09:39
  • Sure, you still need to check if you want an assignment to happen or not. If you use '=' operator, something will inevitably be assigned, be it a data, an undefined or null. So the above is just a safe property access. Does conditional assignment operator even exists, in any language? – yagger Nov 15 '16 at 10:38
  • Of course, for example **CoffeeScript**, it also has `||=` – ᆼᆺᆼ Nov 15 '16 at 15:52
  • +1 Even my first thought was `(((a.b || {}).c || {}).d || {}).e`. But more precise it would be `((((a || {}).b || {}).c || {}).d || {}).e` – IsmailS Sep 19 '19 at 19:03
5

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.

Bergi
  • 513,640
  • 108
  • 821
  • 1,164
  • That looked promising, however at least what Babel does with it is not any different from just `aThing = possiblyNull.thing` – ᆼᆺᆼ Aug 21 '15 at 17:20
  • 1
    @PeterVarga: Oops, you're right, destructuring works when the property is not existent, but not when the object is `null`. You'd have to supply a default value, pretty much like [this pattern](http://stackoverflow.com/a/4034468/1048572) but with more confusing syntax. – Bergi Aug 22 '15 at 19:52
  • This answer is dated - the null safe operators were officially added to ES6 ~10 months ago, and most browsers supported it for ~a year before it was official. – ArtOfWarfare Dec 28 '20 at 16:16
  • @ArtOfWarfare They were not added to ES6 (ES2015) but to ES2020, so the answer is still factually correct and answering the question, which asked about ES6 only. Updated anyway :-) – Bergi Dec 28 '20 at 16:23
3

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.

SE_net4 the downvoter
  • 21,043
  • 11
  • 69
  • 107
Antimony
  • 33,711
  • 9
  • 88
  • 96
1
// 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);
Gary Fu
  • 11
  • 1
0

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('.'))  
prototype
  • 6,440
  • 12
  • 48
  • 86
-5

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

Felipe
  • 7,049
  • 3
  • 26
  • 40
  • 2
    With this you are *de facto* writing code into strings. It's really a bad idea to do that. It makes your code impossible to refactor and not IDE-friendly. – tocqueville Aug 20 '18 at 16:40