io-ts
io-ts copied to clipboard
Type error with Kliesli composition if Error types don't match up and also importing normal Decoder/Encoder
🐛 Bug report
Current Behavior
In Master (2.2.8) there's an error when composing with stringD as the dependency.
import * as NEA from 'fp-ts/lib/NonEmptyArray'
const V = <E>() => getValidation(NEA.getSemigroup<E>());
const stringD = K.fromRefinement( V<NotString>())(G.string.is, () => [NotString()]);
const stringD2 = pipe(stringD, K.refine(V<NotString | EmptyString>())(
(s: string): s is NonEmptyString => s.length > 0,
() => [EmptyString()]
))
Type error, and inference fails:
Argument of type 'Kleisli<"Either", unknown, NonEmptyArray<DomainEvent<"NotString", unknown>>, string>' is not assignable to parameter of type 'Kleisli<"Either", unknown, NonEmptyArray<DomainEvent<"NotString", unknown> | DomainEvent<"EmptyString", unknown>>, string>'.
Essentially stringD can return NotString as the error type, whereas stringD2 can return NotString or EmptyString as the error type, and in 2.2.8 composing these two decoders fails with type error with the typing the way it is, but in 2.2.7 it works.
Expected behavior
In 2.2.7, there's no type error with this use case.
Reproducible example
Suggested solution(s)
Not sure what's causing it as there doesn't seem to be any changes to the Kliesli implementation and also the expected "from" type for K.parse doesn't seem to change (nor the typing of the stringD type).
Additional context
Your environment
| Software | Version(s) |
|---|---|
| io-ts | 2.2.8 |
| fp-ts | |
| TypeScript | 3.9.2 |
So stringD is a decoder that can return the "NotString" error type only.
If I compose the decoder so that some new decoder stringD2 depends on stringD and refines it further, and now it can return the errors "NotString" and "EmptyString", this does not work in 2.2.8.
Previously in 2.2.7 it would work (no type errors), and it would infer the potential error types of the composed decoder.
For example, if I did
const decoded = stringD2.decode('some string');
if (decoded._tag === 'Left') {
switch(decoded.left) {
// Would get typescript hints for the value of decoded.left
}
}
Because this doesn't work now, I have to use a NonEmptyArray semigroup that can hold all potential errors, even irrelevant errors to the specific decoder I'm using.
@chrischen please include a repro
EDITED: Sorry forgot to save earlier. This one has the correct implementation.
https://codesandbox.io/s/hungry-sky-7bx2r?file=/src/index.ts
I can't seem to get the error to show in this, but it gives typescript error in VS code and in my Vim setup.
So it seems like the reason codesandbox is not showing the TS error is because it's using an older version of typescript (not sure which version, but I downgraded to 3.4 and these errors don't show up on my local system—same as when downgrading to io-ts 2.2.7 but with latest typescript).
I just tried to reproduce in a clean project (with same compile settings for typescript) and the problem does not appear to exist. Currently investigating to see why the TS errors are different.
Ok I finally found what was causing the typescript error. It turns out that if you import either of these two anywhere in your typescript project while also importing and using the Kleisli decoder, it will cause the aforementioned type errors. This was especially confusing because it was causing the typescript error even in an isolated test file that did not import the normal Decoder/Encoder.
I updated the codesandbox to reproduce this (just comment out the the Decoder/Codec import to see the error vanish: https://codesandbox.io/s/hungry-sky-7bx2r?file=/src/index.ts
import * as D from 'io-ts/lib/Decoder';
import * as C from 'io-ts/lib/Codec';