artch
artch

Reputation: 4545

Question mark syntax from CoffeeScript without CoffeeScript

CoffeeScript has such syntax sugar:

item.getFoo?().fooParam?.bar

Which translates into long javascript equivalent with getFoo==null and fooParam==null checks. The question is: are there any ways to use this syntax in vanilla javascript with a library/translator/compiler other than CoffeeScript? We use Traceur in our project, but it doesn't have such syntax because it is not ES6 compliant (although I wish it to). Maybe some way to implement it within Traceur fork?

Upvotes: 12

Views: 2328

Answers (3)

Andri
Andri

Reputation: 567

The optional chaining operator ?. was introduced in ES2020.

obj.val?.prop
obj.val?.[expr]
obj.arr?.[index]
obj.func?.(args)

It is supported by the browsers of 91.81% of internet users as of 29 November 2021 according to https://caniuse.com/mdn-javascript_operators_optional_chaining.

Upvotes: 0

willlma
willlma

Reputation: 7543

I had this same question recently, and I came here hoping for a better solution than my current one. If you're doing this frequently, it's easier to make a function to do it for you:

var qm = function(arg) {
  if (arg instanceof Object) return arg;
  return function(){};
};

Then to use it, you wrap your objects in it to make sure no error is raised. It starts to look ugly if there are many question marks on a line

qm(qm(item.getFoo)().fooParam).bar

Upvotes: 1

mattbasta
mattbasta

Reputation: 13709

If you don't want the exact CoffeeScript semantics, you can cheat a bit:

return item.getFoo ? (item.getFoo().fooParam || {}).bar : undefined;

There are a few tricks going on here.

  1. The ternary operator is used to test the truthiness of item.getFoo
  2. If fooParam is missing, falsey, or absent, we substitute it with an empty object. CoffeeScript would have bailed out here.
  3. We return the value of bar regardless of whether it exists. If it does exist, you get the value you want. If it doesn't exist but fooParam is set, you get undefined. If it doesn't exist because fooParam was undefined and we fell back to {}, you still get undefined.

You can write some helpers if the ternary operator gets in the way:

function defaultObject(input) { // A helper to put somewhere
    return input || {};
}

return defaultObject((item.getFoo || defaultObject)().fooParam).bar;

This is even trickier: defaultObject will return {} when called with getFoo, so you don't need a ternary operator around the function call. If fooParam isn't truthy, defaultObject will return another empty object, eliminating the need for another ||. If fooParam is truthy, defaultObject behaves like the identity function and returns it.

I'm sure this could be golfed further down, but I'd recommend avoiding this pattern. Anyone reading your code will be fairly confused and blame you for making a mess in the codebase.

Upvotes: 4

Related Questions