TypeScript Compiler Throwing Unresolvable Error

Hello all,

TypeScript is doing my head in, by seemingly not understanding my apps conditions:

error: TS2345 [ERROR]: Argument of type '(pull: isMerged) => MergeCommit' is not assignable to parameter of type '(value: Pull, index: number, array: Pull[]) => MergeCommit'.
  Types of parameters 'pull' and 'value' are incompatible.
    Type 'Pull' is not assignable to type 'isMerged'.
      Type 'isNotMerged' is not assignable to type 'isMerged'.
        The types of 'pullRequest.merged' are incompatible between these types.
          Type 'false' is not assignable to type 'true'.
      ?.map((pull: isMerged) => pull.pullRequest.mergeCommit);

My code:

type MergeCommit = { additions: number; deletions: number };

type isNotMerged = {
  pullRequest: {
    title: string;
    merged: false;
    mergedAt: null;
    mergeCommit: null;
  };
};

type isMerged = {
  pullRequest: {
    title: string;
    merged: true;
    mergedAt: string;
    mergeCommit: MergeCommit;
  };
};

type Pull = isMerged | isNotMerged;

async function main() {
  try {
    const response = await getContributions();

    const data: Pull[] =
      response.data.user.contributionsCollection.pullRequestContributions.nodes;

    const commits: number = data.filter((pull) => pull.pullRequest.merged)
      .length;

    const mergeCommits: MergeCommit[] = data
      .filter((pull: Pull) => pull.pullRequest.merged)
      .map((pull: isMerged) => pull.pullRequest.mergeCommit);

    const additions: NorU = mergeCommits
      .map((com: MergeCommit) => com?.additions)
      .reduce((accu: number, curr: number) => curr + accu);

    const deletions: NorU = mergeCommits
      .map((com: MergeCommit) => com?.deletions)
      .reduce((accu: number, curr: number) => curr + accu);
  }
}

In my mind, my logic clearly states that the types are assignable. Is there something I am missing about how the compiler can misinterpret .filter/.map arguements?

Any assistance is appreciated

I could share more code, but the type declarations are quite exhaustive

So so you have a union type Pull, which may be isMerged or isNotMerged.

You’re filtering an array of type Pull[].

You know that what you’re going to do is filter down to just the merged items, but the compiler is inferring the signature of the callback as this:

(pull: Pull) => Pull

So when you then map, you’re attempting to only access the property merge.mergeCommit, which is on type isMerged.

You are trying to map an array of type isMerged[] but you have an array of type Pull[].

This is why you’re getting that error. You’re saying the map is over isMerged[], but type inference says it’s over Pull[]. The only way the.compiler could square that circle would be to read any items of type isNotMerged as type isMerged.

It can’t do that because on isNotMerged the type of the merged property is the literal false and on isMerged it’s true, so they are incompatible.

If the property merged was of type boolean, then the two interfaces would be functionally identical, so I think TS would let it run fine. But that would defeat the point, so:

On that filter callback, you need to narrow the return type to be isMerged. If that’s done, the compiler should now be happy that the type of the array returned from filter is isMerged[], so you should then have an array of that type to map over.

2 Likes

Right. That does make sense. Thank you.

I thought I was doing this with:

.map((pull: isMerged) =>

I got the answer now.

Thank you, so much, Dan.

Sorry for wall of text! It was mainly so that I could walk through on my head what the compiler was doing