• Start Date: 2019-11-08
  • Relevant Team(s): Ember.js
  • RFC PR: https://github.com/emberjs/rfcs/pull/554
  • Tracking: (leave this empty)

Deprecate getWithDefault

Summary

Deprecate support for getWithDefault in Ember's Object module (@ember/object) – both the function and the class method – because its expected behaviour is confusing to Ember developers.

Motivation

The problem with getWithDefault is that its behaviour is confusing to Ember developers. The API will only return the default value when the value of the property retrieved is undefined. This behaviour is often overlooked when using the function where a developer might expect that null or other falsy values will also return the default value.

Given the JavaScript language will soon (currently in Stage 3) give us the appropriate tool for this use case using the Nullish Coalescing Operator ??, we can deprecate usage of getWithDefault and use that instead.

Transition Path

Ember will start logging deprecation messages for getWithDefault usage.

We can codemod our current usage of getWithDefault with the equivalent behaviour using plain JavaScript. The migration guide will cover this example:

Before:

import { getWithDefault } from '@ember/object';

let result = getWithDefault(obj, 'some.key', defaultValue);

After:

import { get } from '@ember/object';

let result = get(obj, 'some.key');
if (result === undefined) {
  result = defaultValue;
}

Using Nullish Coalescing Operator

We cannot codemod directly into the nullish coalescing operator since the expected behaviour of getWithDefault is to only return the default value if it is strictly undefined. The nullish coalescing operator accepts either null or undefined to show the default value.

The function getWithDefault will not return the default value if the provided value is null. The function will only return the default value for undefined:

let defaultValue = 1;
let obj = {
  nullValue: null,
  falseValue: false,
};

// Returns defaultValue 1, undefinedKey = 1
let undefinedValue = getWithDefault(obj, 'undefinedKey', defaultValue);

// Returns null, nullValue = null
let nullValue = getWithDefault(obj, 'nullValue', defaultValue);

// Returns obj's falseValue, falseValue = false
let falseValue = getWithDefault(obj, 'falseValue', defaultValue);

The nullish coalescing operator (??) will return the default value when the provided value is undefined or null:

let defaultValue = 1;
let obj = {
  nullValue: null,
  falseValue: false,
};

// Returns defaultValue 1, undefinedKey = 1
let undefinedValue = get(obj, 'undefinedKey') ?? defaultValue;

// Returns defaultValue 1, nullValue = 1
let nullValue = get(obj, 'nullValue') ?? defaultValue;

// Returns obj's falseValue, falseValue = false
let falseValue = get(obj, 'falseValue') ?? defaultValue;

This can be an option if we are aware that either null or undefined should return the default value.

Tooling Support:

  • Babel already supports the nullish coalescing operator so we can use that for future use cases where we need to check if a property is null or undefined before applying a default value.

  • TypeScript, similarly, as of version 3.7 also supports the operator so we will not be breaking that flow either.

Using Object Destructuring With Defaults

If we would like to return the default value if the existing value is undefined we can also use object destructuring with defaults.

Object destructuring with defaults will return the default value when the provided value is undefined:

let defaultValue = 1;
let obj = {
  nullValue: null,
  falseValue: false,
};

// Returns defaultValue 1, undefinedKey = 1
let { undefinedKey = defaultValue } = obj;

// Returns defaultValue 1, nullValue = null
let { nullValue = defaultValue } = obj;

// Returns obj's falseValue, falseValue = false
let { falseValue = defaultValue } = obj;

How We Teach This

Add the transition path to the Ember Deprecation Guide.

The references to getWithDefault will need to be removed from the API docs.

There are no changes needed for the Ember Guides since we do not use it anywhere.

Drawbacks

The downside to deprecating getWithDefault would be an increase to the line length of component files that use it. This change will also cause some deprecation noise but could be mitigated with a codemod.

Alternatives

Adding null as a condition

We could add null as a condition alongside undefined which would return the default value provided. This is similar to what is proposed in Nullish Coalescing for JavaScript. This would however still be a breaking change since people who are depending on getWithDefault to work the way it does for null today will be broken if we change it.

Do nothing

We could keep support in place, and provide more guidance around using it. There are already some articles cautioning usage of getWithDefault when dealing with null or falsy values.

Unresolved questions

None at the moment.