supermacro / neverthrow

Type-Safe Errors for JS & TypeScript

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Combine types not inferred properly for homogenous ResultAsync list

rhlsthrm opened this issue · comments

I have code similar to the following:

result.andThen(value => {
  // ...
  return combine([
    ResultAsync.fromPromise(
      getBalance(senderAddress), // returns Promise<BigNumber>
      (err) => new Error(...),
    ),
    ResultAsync.fromPromise(
      getBalance(receiverAddress), // returns Promise<BigNumber>
      (err) => new Error(...),
    ),
  ]);
}).andThen(([senderBalance, receiverBalance]) => {
  // senderBalance and receiverBalance should both be typed as BigNumber
});

When I try this code, I get type errors:

Argument of type '([senderBalance, receiverBalance]: [any, any]) => ResultAsync<unknown, HandlerError> | ResultAsync<undefined, unknown>' is not assignable to parameter of type '(t: unknown) => Result<unknown, unknown> | ResultAsync<unknown, unknown>'.
  Types of parameters '__0' and 't' are incompatible.
    Type 'unknown' is not assignable to type '[any, any]'.ts(2345)

Am I doing something wrong? If I change the last andThen to the following it works:

.andThen((balances) => {
  const [senderBalance, receiverBalance] = balances as BigNumber[];
  // ...
});

I don't believe I should have to cast it since the combine should give me the proper result with an array of values.

commented

This Comment Is Kind Of Irrelevant. Safe To Skip To Next Comment.

The following comment is regarding heterogeneous lists and I later realized that we were talking about homogeneous lists. Keeping this here for context anyways.


This is a known issue which I have not had the time to address ... #226 provides some (somewhat outdated) context on the problem. This comment summarizes the discussion.

I am open to addressing the issue (given I have time, or someone else is able to address this sooner than me) under a MAJOR change for the package.


Anyways, the current way that I work around this "limitation" is to declare lists as const so as to turn them into tuples.

  return combine([
    ResultAsync.fromPromise(
      getBalance(senderAddress), // returns Promise<BigNumber>
      (err) => new Error(...),
    ),
    ResultAsync.fromPromise(
      getBalance(receiverAddress), // returns Promise<BigNumber>
      (err) => new Error(...),
    ),
  ] as const);

Here is another example:

  return combine([
    functionThatReturnsOneResultAsync(arg1),
    functionThatReturnsDifferentResultAsync(arg2),
  ] as const)

Info on "const assertions":

https://mariusschulz.com/blog/const-assertions-in-literal-expressions-in-typescript

commented

One thing I missed for your example is that getBalance(senderAddress) and getBalance(receiverAddress) both return Promise<BigNumber> ... so you should have a ResultAsync<BigNumber, Error>[].

I would definitely classify this as a bug. I'm a bit confused as to why that is occurring.

Attempt To Reproduce

I tried reproducing your issue using version 4.2.1:

Screen Shot 2021-07-22 at 4 04 19 PM

But as you can see I was not able to reproduce.

This is the tsconfig I used:

{
  "compilerOptions": {
      "module": "CommonJS",
      "noImplicitAny": true,
      "sourceMap": true,
      "esModuleInterop": true,
      "allowJs": false,
      "allowSyntheticDefaultImports": true,
      "downlevelIteration": true,
      "noUnusedLocals": true,
      "noUnusedParameters": true,
      "noFallthroughCasesInSwitch": true,
      "forceConsistentCasingInFileNames": true,
      "strictNullChecks": true,
      "strictFunctionTypes": true,
      "strictBindCallApply": true,
      "strictPropertyInitialization": true,
      "baseUrl": "./src",
      "lib": [
        "dom",
        "es2016",
        "es2017.object"
      ],
      "outDir": "build"
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
      "node_modules",
      "**/*.spec.ts"
  ]
}

Ok this is weird, I can't reproduce it when I pull it out and start with the combine, but when the combine is part of a larger chain of .andThen that's where I'm getting issues. Is there any way you can take a look at my implementation @supermacro : https://github.com/connext/nxtp/blob/main/packages/router/src/handler.ts#L193?

I'd love to know if I'm doing something wrong, which is quite likely.

commented

There's a lot going on in that file 😅 I could try and reproduce the issue based on what you mentioned here:

but when the combine is part of a larger chain of .andThen that's where I'm getting issues

commented

@rhlsthrm did you ever figure out why this issue was occurring? If not, I think the best course of action might be to hop on a call and review the code together.

commented

Closing because of lack of response from @rhlsthrm, feel free to reopen though!