How to change values of data returned from imperative Apex?

Currently lightning datatable has a bug - it does not rerender data when an object in the array data changes or by using push to modify data, so you need to explicitly reassign data for datatable to rerender. For better understanding you can check this playground link

Try below:

getContactListJS() {
    getContactListApex().then(result => {
        this.data = [...result].map(record => {
            record.FirstName = record.FirstName + 'aaa';
            return record;
        });
    }).catch(error => {
        this.error = error;
    });
}

To add to @salesforce-sas's answer, I'm not sure I would classify this as a bug in datatable, per se, but rather how LWC's tracked and api properties work in general. If you have an object or array as a property and you only update the content then LWC doesn't notice the changes and does not trigger re-rendering (or wire calls etc.).

There are two different solutions to this, depending on the use case:

  1. Explicitly replace the tracked or api property value, as is the case in salesforce-sas's answer, with the updated content
  2. If you are using an object (not an array - so this doesn't apply in this question's scenario) then you can explicitly initialize the object in its declaration so the nested properties to be tracked (or made "reactive") are enumerated. By doing the latter you inform the LWC compiler that you want changes to the declared nested properties (and only those ones) to be considered as reactive.

The first solution, which is applicable here, has been covered in the previous answer. The second solution (relevant in other use cases) looks like the following.

In the LWC, declare the tracked/api property with dummy object containing the reactive sub-properties:

@track
data = {
    example1: undefined,
    example2: "some default"
}

In the update code just assign to the sub-properties:

this.data.example1 = result.data.something;

Use of the this.data property or its content elsewhere, such as in the template or in parameters to wired services etc. will then operate as you expect. On the other hand, if you omit the initialization with a dummy (or miss out explicit declaration of a sub-property in that dummy in the property declaration) then the component won't react to changes to the relevant sub-properties within this property. Of course, you can always fall back on solution 1.