1

I have the error mentioned in the title for the line transactionIds: acc.transactionIds.push(currId).

My code looks like this:

const resultObject: {
        amountAccumulated: number;
        amountLeft: number;
        rate: number | undefined;
        transactionIds: string[];
    } = arrDocs.reduce(
        (acc, curr, i) => {
            let currData:
                | admin.firestore.DocumentData
                | undefined = curr.data();
            if (currData === undefined) return acc;

            let currId = curr.id;
            let amountLeft: number = acc.amountLeft;
            let amountToAdd: number = Math.min(
                currData.remaining_amount,
                amountLeft
            );

            return {
                amountAccumulated: acc.amountAccumulated + amountToAdd,
                amountLeft: acc.amountLeft - amountToAdd,
                rate: undefined,
                transactionIds: acc.transactionIds.push(currId)
            };
        },
        {
            amountAccumulated: 0,
            amountLeft: spentAmount.amount,
            rate: undefined,
            transactionIds: []
        }
    );

I don't know why I have the error. Anyone with an idea? I read online that I should just declare the array (which I thought I have done with the declaration of the resultObject).

Edit: When I implement the suggested change using the spread operator, I receive the following error:

TS2345: Argument of type '(acc: { amountAccumulated: number; amountLeft: number; rate: undefined; transactionIds: never[]; ...' is not assignable to parameter of type '(previousValue: { amountAccumulated: number; amountLeft: number; rate: undefined; transactionIds:...'.
      Type '{ amountAccumulated: number; amountLeft: number; rate: undefined; transactionIds: string[]; }' is not assignable to type '{ amountAccumulated: number; amountLeft: number; rate: undefined; transactionIds: never[]; }'.
        Types of property 'transactionIds' are incompatible.
          Type 'string[]' is not assignable to type 'never[]'.
            Type 'string' is not assignable to type 'never'.

Here the code:

return {
     amountAccumulated: acc.amountAccumulated + amountToAdd,
     amountLeft: acc.amountLeft - amountToAdd,
     rate: undefined,
     transactionIds: [...acc.transactionIds, currId]
 };
Spurious
  • 1,903
  • 5
  • 27
  • 53
  • I've never seen a type declaration start with a `|` before. What does `let currData: | admin.firestore.DocumentData | undefined` mean? – T.J. Crowder Jun 20 '19 at 10:46
  • That is a good question and it wasn't my intention, but prettier adds it when I reformat it. It's weird, but I can't remove it and it doesn't raise an error. – Spurious Jun 20 '19 at 10:49

1 Answers1

3

I can't explain why the error says quite what it says, but that line is incorrect. push returns a number, not an array, but transactionIds is supposed to be an array.

If you want to create a new object every time, you can spread out the existing transaction IDs and add the new one like this:

transactionIds: [...acc.transactionIds, currId]

You've said you're having trouble getting this to work and that TypeScript is still complaining about a never[] array. Since you're requiring TypeScript to infer the accumulator's transactionIds type, I guess it must be inferring wrong.

I'd define a type:

interface ResultType {
    amountAccumulated: number;
    amountLeft: number;
    rate: number | undefined;
    transactionIds: string[];
}

and then use it on the object you're providing for the accumulator, so that it's clear to TypeScript what the type of transactionIds is:

const resultObject: ResultType = arrDocs.reduce(
    (acc, curr, i) => {
        // ...
    },
    <ResultType>{
        amountAccumulated: 0,
        amountLeft: spentAmount.amount,
        rate: undefined,
        transactionIds: []
    }
);

As a side note, using reduce for this does nothing but make it a lot more complicated. You also have a lot of redundant type annotations that TypeScript will be perfectly happy to infer (correctly). Here's how I'd approach it:

First, I'd have a type (although if you prefer not to, you don't have to):

interface ResultType {
    amountAccumulated: number;
    amountLeft: number;
    transactionIds: string[];
    rate: number | undefined;
}

Then I'd do it like this:

let amountAccumulated = 0;
let amountLeft = 0;
let transactionIds: string[] = [];
for (const curr of arrDocs) {
    let currData = curr.data();
    if (currData !== undefined) {
        let amountToAdd = Math.min(
            currData.remaining_amount,
            acc.amountLeft
        );
        amountAccumulated += amountToAdd;
        amountLeft -= amountToAdd;
        transactionIds.push(curr.id);
    }
}
const resultObject: ResultType = {
    amountAccumulated,
    amountLeft,
    rate: undefined,
    transactionIds
};

If you don't want to have a type, just replace it in that last statement creating the object:

const resultObject: {
    amountAccumulated: number;
    amountLeft: number;
    transactionIds: string[];
    rate: number | undefined;
} = {
    amountAccumulated,
    amountLeft,
    rate: undefined,
    transactionIds
};
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    or use `concat` which returns a new array. – Murat Karagöz Jun 20 '19 at 10:42
  • `acc.transactionIds.concat(currId)` does give the same error btw. – Spurious Jun 20 '19 at 10:46
  • @Spurious - `transactionIds: acc.transactionIds.concat(currId)` should work just fine. `concat` also returns an array. – T.J. Crowder Jun 20 '19 at 10:47
  • It somehow doesn't and the error might be somewhere else. I cannot get the solution with the spread operator to work. – Spurious Jun 20 '19 at 10:48
  • I've just added the error message I receive when I add the spread operator solution. – Spurious Jun 20 '19 at 10:51
  • @Spurious - I don't know why it thinks `transactionIds` is `never[]`, but maybe you should type the array you're passing in as the accumulator, rather than letting TS infer the type. – T.J. Crowder Jun 20 '19 at 10:56
  • How could I do this? I was trying, but I was failing. – Spurious Jun 20 '19 at 10:58
  • @Spurious - I've updated the answer, and also suggested an alternative approach. – T.J. Crowder Jun 20 '19 at 11:08
  • 1
    Thank you for this. I have updated my code using the added type. Thank your for the help and tipps for the code change. I must say that I want to do something differently than my code shows. I have started with my code this way, because I wanted to see if I run into errors (which I did). Nonetheless, I will have a close look at your suggestions and probably implement parts of it. – Spurious Jun 20 '19 at 11:14