Deprecations Added in Ember 5.x

What follows is a list of deprecations introduced to Ember during the 5.x cycle.

For more information on deprecations in Ember, see the main deprecations page.

Deprecations Added in 5.3.0

Implicit Route Model

until: 6.0.0
id: deprecate-implicit-route-model

Previously, if no Route#model hook was specified and a _id parameter was present, Ember would attempt to figure out how to load that model for you. Specify your own model hook to load from the store, if desired.

An optional feature, called no-implicit-route-model, can be turned on to clear this deprecation and opt in to the new behaviour. This optional feature is enabled in blueprints as of v5.7.0 and will be removed in v6.0.0. For more information, see the optional features guides.

For example:

import { Route } from '@ember/routing/route';
import { service } from '@ember/service';

export default class MyModelRoute extends Route {
  @service store;

  model({ my_model_id }) {
    return this.store.findRecord('my-model', my_model_id);
  }
}

For more background, read the RFC.

Deprecations Added in 5.9.0

Action helper and modifier

until: 6.0.0
id: template-action

Scenario: action is passed a string

Before:

<button type="button" {{action "plusOne"}}>
  Click Me
</button>

After:

<button type="button" {{on 'click' this.plusOne}}>
  Click Me
</button>

or, if plusOne is passed in as an argument:

<button type="button" {{on 'click' @plusOne}}>
  Click Me
</button>

If the plusOne action is in an actions object, it needs to move out:

For Glimmer components

Before:

import Component from '@glimmer/component';

export default class Demo extends Component {
    actions = {
        plusOne() {
           /* ... */ 
        }
    }
}

After:

import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class Demo extends Component {
    @action
    plusOne() {
       /* ... */ 
    }
}

or

For Classic Components with native classes

Before:

import Component from '@ember/component';

export default class Demo extends Component {
    doMath() {
      this.send('plusOne');
    }

    actions = {
        plusOne() {
           /* ... */ 
        }
    }
}

After:

import Component from '@ember/component';
import { action } from '@ember/object';

export default class Demo extends Component {
    doMath() {
      this.plusOne();
    }

    @action
    plusOne() {
       /* ... */ 
    }
}

or

For Classic Components with EmberObject.extend

Before:

import Component from '@ember/component';

export default Component.extend({
    actions: {
        plusOne() {
           /* ... */ 
        }
    }
})

After:

import Component from '@ember/component';
import { action } from '@ember/object';

export default Component.extend({
  plusOne: action(function() {
      /* ... */ 
  }),
})

If (action) or {{action}} is passed a string, it's possible that the referenced method is declared on the caller, and not the immediate component -- that is, (action) and {{action}} bubble up the render tree from route templates -> controllers -> routes.

Note that @action is completely different from (action) or {{action}} (and is partly a motivator for deprecating (action) and {{action}}, to reduce ambiguity).

@action binds the this on the method to the instance of the class.

Scenario: action is passed a function reference

Before:

<SomeComponent @update={{action this.plusOne}} />

After

<SomeComponent @update={{this.plusOne}} />

Scenario: action is passed parameters

Before:

<SomeComponent @update={{action this.plus 1}} />

After:

<SomeComponent @update={{fn this.plus 1}} />

Scenario: action is used with mut

Before:

<SomeComponent @update={{action (mut @value.property}} />

After:

// parent.js
import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class SomeComponent extends Component {
    @action
    handleUpdate(value) {
        this.args.property = value; 
    }
}
{{! parent.hbs }}
<SomeComponent @update={{this.handleUpdate}} />

Related, Combining function arguments with action functions

For more background, read the RFC

Deprecations Added in 5.10.0

Component Template Resolving

until: 6.0.0
id: component-template-resolving

There are two types of paths to migrate off the old layouts

  • use a currently supported multi-file layout (keeping separate js, ts, and hbs files)
  • migrate the component entirely to the latest component format, gjs, gts, (aka <template>)

There are some tools to help with this:

Specifically, these layouts are no longer supported:

ClassicPods
{app,addon}/
  components/
    foo.js
    namespace/
      bar.js
  templates/
    components/
      foo.hbs
      namespace/
        bar.hbs
{app,addon}/
  components/
    foo/
      component.js
      template.hbs
    namespace/
      bar/
        component.js
        template.hbs

The above example(s) can be migrated to:

{app,addon}/
  components/
    foo.js 
    foo.hbs
    namespace/
      bar.js
      bar.hbs

Or using --component-structure=nested

{app,addon}/
  components/
    foo/
      index.js 
      index.hbs
    namespace/
      bar/
        index.js
        index.hbs

Note, however, that classic components importing the layout and setting it on an @ember/component will still work. The key thing being deprecated is the runtime resolution of templates, so if there is an import involved, there is no runtime resolution.

Deprecate array prototype extensions

until: 6.0.0
id: deprecate-array-prototype-extensions

Ember historically extended the prototypes of native Javascript arrays to implement Ember.Enumerable, Ember.MutableEnumerable, Ember.MutableArray, Ember.Array. As of v5, the usages of array prototype extensions are deprecated.

For convenient functions like filterBy, compact, you can directly convert to use native array methods.

For mutation functions (like pushObject, replace) or observable properties (firstObject, lastObject), in order to keep the reactivity, you should take following steps:

  1. convert the array either to a new @tracked property, or use TrackedArray from tracked-built-ins;
  2. use array native methods;
  3. fully test to make sure the reactivity is maintained.

Convenient Functions

For convenient functions like filterBy, compact, you can directly convert to use native array methods. This includes following (a list from EmberArray methods):

any

Before:

someArray.any(callbackFn);

After:

someArray.some(callbackFn);

compact

Before:

someArray.compact();

After:

someArray.filter(val => val !== undefined && val !== null);

filterBy

Before:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.filterBy('food', 'beans'); // [{ food: 'beans', isFruit: false }]

After:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.filter(el => el.food === 'beans'); // [{ food: 'beans', isFruit: false }]

findBy

Before:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.findBy('isFruit'); // { food: 'apple', isFruit: true }

After:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.find(el => el.isFruit); // { food: 'apple', isFruit: true }

getEach

Before:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.getEach('food'); // ['apple', 'beans']

After:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.map(el => el.food); // ['apple', 'beans']

invoke

Before:

class Person {
  name;

  constructor(name) {
    this.name = name;
  }

  greet(prefix = 'Hello') {
    return `${prefix} ${this.name}`;
  }
}

[new Person('Tom'), new Person('Joe')].invoke('greet', 'Hi'); // ['Hi Tom', 'Hi Joe']

After:

class Person {
  name;

  constructor(name) {
    this.name = name;
  }

  greet(prefix = 'Hello') {
    return `${prefix} ${this.name}`;
  }
}

[new Person('Tom'), new Person('Joe')].map(person => person['greet']?.('Hi')); // ['Hi Tom', 'Hi Joe']

isAny

Before

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.isAny('isFruit'); // true

After:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.some(el => el.isFruit); // true

isEvery

Before:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.isEvery('isFruit'); // false

After:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.every(el => el.isFruit); // false

mapBy

Before:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.mapBy('food'); // ['apple', 'beans']

After:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.map(el => el.food); // ['apple', 'beans']

objectAt

Before

const someArray = [1, 2, 3, undefined];
someArray.objectAt(1); // 2

After:

const someArray = [1, 2, 3, undefined];
someArray[1] // 2

objectsAt

Before:

const someArray = [1, 2, 3, undefined];
someArray.objectsAt([1, 2]); // [2, 3]

After:

const someArray = [1, 2, 3, undefined];
[1, 2].map(index => someArray[index]); //[2, 3]

reject

Before:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.reject(el => el.isFruit); // [{ food: 'beans', isFruit: false }]

After:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.filter(el => !el.isFruit); // [{ food: 'beans', isFruit: false }]

rejectBy

Before:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.rejectBy('isFruit'); // [{ food: 'beans', isFruit: false }]

After:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.filter(el => !el.isFruit); // [{ food: 'beans', isFruit: false }]

sortBy

Before:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.sortBy('food', 'isFruit'); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]

After:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
[...someArray].sort((a, b) => {
  return a.food?.localeCompare(b.food)
    ? a.food?.localeCompare(b.food)
    : a.isFruit - b.isFruit;
}); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]

toArray

Before:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.toArray(); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]

After:

const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
[...someArray] // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]

uniq

Before:

const someArray = [1, 2, 3, undefined, 3];
someArray.uniq(); // [1, 2, 3, undefined]

After:

const someArray = [1, 2, 3, undefined, 3];
[...new Set(someArray)] // [1, 2, 3, undefined]

uniqBy

Before:

const someArray = [{ food: 'apple' }, { food: 'beans' }, { food: 'apple' }];
someArray.uniqBy('food'); // [{ food: 'apple' }, { food: 'beans' }]

After:

const someArray = [{ food: 'apple' }, { food: 'beans' }, { food: 'apple' }];
someArray.reduce(
  (unique, item) => {
    if (!unique.find(i => item.food === i.food)) {
      unique.push(item);
    }
    return unique;
  },
  []
); // [{ food: 'apple' }, { food: 'beans' }]

You may also instead rely on methods from another library like lodash. Keep in mind that different libraries will behave in slightly different ways, so make sure any critical transformations are thoroughly tested.

Some special cases

without

Before

const someArray = ['a', 'b', 'c'];
someArray.without('a'); // ['b', 'c']

After

const someArray = ['a', 'b', 'c'];
someArray.filter(el => el !== 'a'); // ['b', 'c']

Please make sure without reactivity is fully tested.

setEach

setEach method internally implements set which responds to reactivity. You can either also use set or convert to @tracked properties.

Before

const items = [{ name: 'Joe' }, { name: 'Matt' }];

items.setEach('zipCode', '10011'); // items = [{ name: 'Joe', zipCode: '10011' }, { name: 'Matt', zipCode: '10011' }]

After

// use `set`
import { set } from '@ember/object';

const items = [{ name: 'Joe' }, { name: 'Matt' }];

items.forEach(item => {
  set(item, 'zipCode', '10011');
}); // items = [{ name: 'Joe', zipCode: '10011' }, { name: 'Matt', zipCode: '10011' }]

or

// use `@tracked`
import { tracked } from '@glimmer/tracking';

class Person {
  name;
  @tracked zipCode;
  constructor({ name, zipCode }) {
    this.name = name;
    this.zipCode = zipCode;
  }
}

const items = new TrackedArray([
  new Person({ name: 'Joe' }),
  new Person({ name: 'Matt' }),
]);

items.forEach(item => {
  item.zipCode = '10011';
}); // items = [{ name: 'Joe', zipCode: '10011' }, { name: 'Matt', zipCode: '10011' }]

Observable Properties

firstObject, lastObject are observable properties. Changing directly from firstObject to at(0) or [0] might cause issues that the properties are no longer reactive.

Used in template

If the firstObject and lastObject are used in a template, you can convert to use get helper safely as get helper handles the reactivity already.

Before

<Foo @bar={{@list.firstObject.name}} />

After

<Foo @bar={{get @list '0.name'}} />

You can also leverage fixers provided by ember-template-lint/no-array-prototype-extensions.

Used in js

If the firstObject and lastObject are used in js files and you used them in an observable way, you will need to convert the accessors to @tracked array or TrackedArray.

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  // lastObj will change when `someAction` is executed
  get lastObj() {
    return this.abc.lastObject;
  }

  @action
  someAction(value) {
    this.abc.pushObject(value);
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  get lastObj() {
    return this.abc.at(-1);
  }

  @action
  someAction(value) {
    this.abc.push(value);
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = [];

  get lastObj() {
    return this.abc.at(-1);
  }

  @action
  someAction(value) {
    this.abc = [...this.abc, value];
  }
}

Mutation methods

Mutation methods are observable-based, which means you should always convert the accessors to @tracked or TrackedArray in order to maintain the reactivity. This includes following (a list from MutableArray methods):

addObject

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  addObject(value) {
    this.abc.addObject(value);
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  addObject(value) {
    if (!this.abc.includes(value)) {
      this.abc.push(value);
    }
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  addObject(value) {
    if (!this.abc.includes(value)) {
      this.abc = [...this.abc, value];
    }
  }
}

addObjects

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  addObjects(value) {
    this.abc.addObjects(value);
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  _addObject(value) {
    if (!this.abc.includes(value)) {
      this.abc.push(value);
    }
  }

  @action
  addObjects(values) {
    values.forEach(v => this._addObject(v))
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  _addObject(value) {
    if (!this.abc.includes(value)) {
      this.abc = [...this.abc, value];
    }
  }

  @action
  addObjects(values) {
    values.forEach(v => this._addObject(v))
  }
}

clear

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  clear(value) {
    this.abc.clear();
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  clear(value) {
    this.abc.splice(0, this.abc.length);
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  clear() {
    this.abc = [];
  }
}

insertAt

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  insertAt(idx, value) {
    this.abc.insertAt(idx, value);
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  insertAt(idx, value) {
    this.abc.splice(idx, 0, value);
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  insertAt(idx, value) {
    this.abc = [...this.abc.slice(0, idx), value, this.abc.slice(this.abc.length - idx)]
  }
}

popObject

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  popObject() {
    this.abc.popObject();
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  popObject() {
    this.abc.pop();
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  popObject() {
    this.abc.pop();
    this.abc = [...this.abc];
  }
}

pushObject

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  pushObject(value) {
    this.abc.pushObject(value);
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  pushObject(value) {
    this.abc.push(value);
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  pushObject(value) {
    this.abc = [...this.abc, value];
  }
}

pushObjects

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  pushObjects(values) {
    this.abc.pushObjects(values);
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  pushObjects(values) {
    this.abc.push(...values);
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  pushObjects(values) {
    this.abc = [...this.abc, ...values];
  }
}

removeAt

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  removeAt(start, len) {
    this.abc.removeAt(start, len);
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  removeAt(start, len) {
    this.abc.splice(start, len);
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  removeAt(start, len) {
    this.abc.splice(start, len);
    this.abc = this.abc;
  }
}

removeObject

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  removeObject(value) {
    this.abc.removeObject(value);
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  removeObject(value) {
    this.abc.filter(item => item !== value);
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  removeObject(value) {
    let loc = this.abc.length || 0;
    while (--loc >= 0) {
      let curValue = this.abc.at(loc);

      if (curValue === value) {
        this.abc.splice(loc, 1);
      }
    }
    this.abc = [...this.abc];
  }
}

removeObjects

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  removeObjects(values) {
    this.abc.removeObjects(values);
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  _removeObject(value) {
    let loc = this.abc.length || 0;
    while (--loc >= 0) {
      let curValue = this.abc.at(loc);

      if (curValue === value) {
        this.abc.splice(loc, 1);
      }
    }
  }

  @action
  removeObjects(values) {
    values.forEach(v => {
      this._removeObject(v);
    });
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  _removeObject(value) {
    let loc = this.abc.length || 0;
    while (--loc >= 0) {
      let curValue = this.abc.at(loc);

      if (curValue === value) {
        this.abc.splice(loc, 1);
      }
    }
    this.abc = [...this.abc];
  }

  @action
  removeObjects(values) {
    values.forEach(v => {
      this._removeObject(v);
    })
  }
}

replace

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  replace(idx, len, values) {
    this.abc.replace(idx, len, values);
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  replace(idx, len, values) {
    this.abc.splice(idx, len, ...values);
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  replace(idx, len, values) {
    this.abc.splice(idx, len, ...values);
    this.abc = [...this.abc];
  }
}

reverseObjects

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  reverseObjects() {
    this.abc.reverseObjects();
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  reverseObjects() {
    this.abc.reverse();
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  reverseObjects() {
    this.abc = [...this.abc.reverse()];
  }
}

setObjects

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  setObjects(values) {
    this.abc.setObjects(values);
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  setObjects(values) {
    this.abc.splice(0, this.abc.length, ...values);
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  setObjects(values) {
    this.abc = [...values];
  }
}

shiftObject

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  shiftObject() {
    this.abc.shiftObject();
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  shiftObject() {
    this.abc.shift();
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  shiftObject() {
    this.abc.shift();
    this.abc = [...this.abc]
  }
}

unshiftObject

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  unshiftObject(obj) {
    this.abc.unshiftObject(obj);
  }
}

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  unshiftObject(obj) {
    this.abc.unshift(obj);
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  unshiftObject(obj) {
    this.abc.unshift(obj);
    this.abc = [...this.abc];
  }
}

unshiftObjects

Before

import Component from '@glimmer/component';
export default class SampleComponent extends Component {
  abc = ['x', 'y', 'z', 'x'];

  @action
  unshiftObjects(objs) {
    this.abc.unshiftObjects(objs);
  }

After

// TrackedArray
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
  abc = new TrackedArray(['x', 'y', 'z', 'x']);

  @action
  unshiftObjects(objs) {
    this.abc.unshift(...objs);
  }
}

or

// @tracked
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class SampleComponent extends Component {
  @tracked abc = ['x', 'y', 'z', 'x'];

  @action
  unshiftObjects(objs) {
    this.abc.unshift(...objs)
    this.abc = [...this.abc];
  }
}

It's always recommended to reference the existing implementation of the method you are trying to convert. This can make sure functionalities are kept as it was. Implementation details can be found in MutableArray, for example removeObject.