unsplash / sum-types-io-ts

io-ts bindings for @unsplash/sum-types.
https://unsplash.github.io/sum-types-io-ts/
MIT License
2 stars 1 forks source link

Should "serialized" imply JSON for member codecs? #27

Open OliverJAsh opened 6 months ago

OliverJAsh commented 6 months ago

In this example we are trying to compose the result of getCodecFromSerialized with JsonFromString. The error makes it difficult to spot which member codec is the culprit.

import * as Sum from '@unsplash/sum-types';
import { getCodecFromSerialized } from '@unsplash/sum-types-io-ts';
import * as t from 'io-ts';
import { JsonFromString } from 'io-ts-types';

type MyUnion =
  | Sum.Member<'A', string>
  | Sum.Member<'B', string>
  | Sum.Member<'C', string | undefined>;
const MyUnion = Sum.create<MyUnion>();

const Codec = getCodecFromSerialized(MyUnion)({
  A: t.string,
  B: t.string,
  C: t.union([t.string, t.undefined]),
});
const CodecFromString = JsonFromString.pipe(
  // Error: Type 'readonly ["A", string] | readonly ["B", string] | readonly ["C", string | undefined]' is not assignable to type 'Json'.
  Codec,
);

If getCodecFromSerialized required Json as the output type of all member codecs, we would have a much nicer error message:

const Codec = getCodecFromSerialized(MyUnion)({
  A: t.string,
  B: t.string,
  // Type 'undefined' is not assignable to type 'Json'.
  C: t.union([t.string, t.undefined]),
});
samhh commented 6 months ago

"Serialized" in sum-types really just means unwrapping the object with its symbols to a plain tuple, giving you the raw data in its least opinionated form. I think that's appropriate as other forms of end-to-end serialization exist besides JSON.

Given this example the second line of the error is a bit narrower:

const ab: Json = null as any as ["A", string] | ["B", undefined]
Type '["A", string] | ["B", undefined]' is not assignable to type 'Json'.
Type '["B", undefined]' is not assignable to type 'Json'.

Given this is likely a common use case then a getCodecFromJSON would probably make sense.