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.
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
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
:
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.
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