Deprecation Guide for @ember/utils package
The @ember/utils
package is now deprecated and will be removed in Ember 7.0.
What is being deprecated
The entire @ember/utils
package, which includes utility functions such as:
compare
isBlank
isEmpty
isEqual
isNone
isPresent
typeOf
Why is this being deprecated
The @ember/utils
package was created as a way to provide utility functions that were previously available on the Ember
global. With the move towards modern JavaScript and the deprecation of the Ember
global, these utility functions are no longer needed as part of the core Ember framework.
Migration path
Option 1: Replace with native JavaScript alternatives (Recommended)
Most utility functions can be replaced with native JavaScript alternatives, which is the preferred approach for better performance and reduced bundle size. See below for examples.
Option 2: Use @ember/legacy-utils (Fallback)
If you need to maintain the exact same API, you can install the @ember/legacy-utils
addon:
ember install @ember/legacy-utils
Then update your imports:
// Before
import { compare, isBlank, isEmpty, isEqual, isNone, isPresent, typeOf } from '@ember/utils';
// After
import { compare, isBlank, isEmpty, isEqual, isNone, isPresent, typeOf } from '@ember/legacy-utils';
Many of these utility functions can be replaced with native JavaScript alternatives:
isBlank
and isEmpty
// Before
import { isBlank, isEmpty } from '@ember/utils';
if (isBlank(value)) { /* ... */ }
if (isEmpty(value)) { /* ... */ }
// After
if (value == null || value === '') { /* ... */ }
if (value == null || value.length === 0) { /* ... */ }
isPresent
// Before
import { isPresent } from '@ember/utils';
if (isPresent(value)) { /* ... */ }
// After
if (value != null && value !== '') { /* ... */ }
isEqual
// Before
import { isEqual } from '@ember/utils';
if (isEqual(a, b)) { /* ... */ }
// After
if (a === b) { /* ... */ }
// or use a deep equality library like lodash.isEqual
typeOf
The typeOf
function provides more detailed type checking than native typeof
. Here are examples for different types:
// Before
import { typeOf } from '@ember/utils';
if (typeOf(value) === 'string') { /* ... */ }
if (typeOf(value) === 'array') { /* ... */ }
if (typeOf(value) === 'date') { /* ... */ }
if (typeOf(value) === 'error') { /* ... */ }
if (typeOf(value) === 'null') { /* ... */ }
// After - Basic types
if (typeof value === 'string') { /* ... */ }
if (Array.isArray(value)) { /* ... */ }
if (value instanceof Date) { /* ... */ }
if (value instanceof Error) { /* ... */ }
if (value === null) { /* ... */ }
// After - More complex type checking
function getType(value) {
if (value === null) return 'null';
if (value === undefined) return 'undefined';
if (Array.isArray(value)) return 'array';
if (value instanceof Date) return 'date';
if (value instanceof RegExp) return 'regexp';
if (value instanceof Error) return 'error';
if (typeof value === 'string') return 'string';
if (typeof value === 'number') return 'number';
if (typeof value === 'boolean') return 'boolean';
if (typeof value === 'function') return 'function';
return 'object';
}
if (getType(value) === 'string') { /* ... */ }
compare
// Before
import { compare } from '@ember/utils';
array.sort((a, b) => compare(a, b));
// After
array.sort((a, b) => {
if (a < b) return -1;
if (a > b) return 1;
return 0;
});
Computed Properties
If you're using @ember/utils
functions in computed properties, you have two migration options:
Option 1: Use tracked with getters (Recommended)
Replace computed properties that use @ember/utils
functions with tracked properties and getters:
// Before
import { computed } from '@ember/object';
import { isBlank, isEmpty } from '@ember/utils';
export default class MyComponent extends Component {
@computed('name', 'description')
get isValid() {
return !isBlank(this.name) && !isEmpty(this.description);
}
}
// After
import { tracked } from '@glimmer/tracking';
export default class MyComponent extends Component {
@tracked name;
@tracked description;
get isValid() {
return this.name != null && this.name !== '' &&
this.description != null && this.description.length > 0;
}
}
Option 2: Use computed properties (Fallback)
If you need to maintain computed properties, you can still use them with native JavaScript:
// Before
import { computed } from '@ember/object';
import { isBlank, isEmpty } from '@ember/utils';
export default class MyComponent extends Component {
@computed('name', 'description')
get isValid() {
return !isBlank(this.name) && !isEmpty(this.description);
}
}
// After
import { computed } from '@ember/object';
export default class MyComponent extends Component {
@computed('name', 'description')
get isValid() {
return this.name != null && this.name !== '' &&
this.description != null && this.description.length > 0;
}
}