HomeToolsAbout a20k

Optional Chaining

What is it

Accesses an object's property or calls a function. If the object accessed or function called using this operator is undefined or null, the expression short circuits and evaluates to undefined instead of throwing an error.

When a property is nullish, the expression is NOT evaluated as a whole.

const adventurer = { name: 'Alice', cat: { name: 'Dinah', }, }; adventurer.dog?.name; // undefined, unevaluated

Use Case

Without optional chaining, looking up a deeply-nested subproperty requires validating the references in between.

const nestedProp = obj.first && obj.first.second;

The value of obj.first is confirmed to be non-nullish before accessing the value of object.first.second

Optional chaining can backfire

# services/location_service.rb ## this line calling the Google Service could return a nil Google::PlacesService.new.place_details(place_id) # invoking methods on nil downstream will result in error

Some Google Places API calls failed and returned nil

  • which, in the location_service, resulted in no method error because it was invoking a method on nil

Using &. optional chaining wouldn’t solve this problem because the attribute ['result'] does not exist on a nil return which means the optional chaining won't solve the core issue of missing attribute, it would silently fail

  • Ruby wouldn’t know how to handle a key retrieval or method call on it

Takeaway

Get specific as possible with Error Handling

  • Avoid catch all without specific error handling
  • When a real error occurs with missing attributes (or missed fetch call), you won’t know what to look for when optional chaining is used

The solution was to write a rescue so the exceptions return empty json instead

# rescue syntax rescue StandardError => e render json: {} end # in the controller def search_zipcode render json: Services::LocationService.match_zipcode(params[:address].to_s) rescue StandardError => e render json: {} end
© VincentVanKoh