gcanti / io-ts

Runtime type system for IO decoding/encoding
https://gcanti.github.io/io-ts/
MIT License
6.68k stars 331 forks source link

Records are stripped when encoding with t.record(NumberFromString, xxx) #709

Open Wolfgang-stack opened 7 months ago

Wolfgang-stack commented 7 months ago

🐛 Bug report

Current Behavior

The most recent version of io-ts (2.2.21) strips all key/value pairs when encoding using t.record with the NumberFromString codec from io-ts-types like this: t.record<NumberFromString, xxx>.

const flagsC = t.record(NumberFromString, t.boolean);
type FlagsC = typeof flagsC;
type Flags = t.TypeOf<FlagsC>;

const myRecord: Flags = {
  "1": true,
  "20": false,
};

const encodeResult = flagsC.encode(myRecord);
// expected result: {1: true, 20: false}
// actual result: {}

Expected behavior

The previous version of io-ts that I was using (2.2.20) did not have the same issue. The expected and previous behavior was:

const encodeResult = flagsC.encode(myRecord); //  {1: true, 20: false}

Reproducible example

https://codesandbox.io/p/sandbox/vibrant-tom-cvxrzp?file=%2Fsrc%2Findex.ts

import * as t from "io-ts";
import { NumberFromString } from "io-ts-types";

const flagsC = t.record(NumberFromString, t.boolean);
type FlagsC = typeof flagsC;
type Flags = t.TypeOf<FlagsC>;

const myRecord: Flags = {
  "1": true,
  "20": false,
};

const encodeResult = flagsC.encode(myRecord);
// expected result: {1: true, 20: false}
// actual result: {}

Suggested solution(s)

I don't have a suggested solution at this time, for now I have just reverted back to io-ts version 2.2.20 and it is working as expected.

Additional context

Your environment

Which versions of io-ts are affected by this issue? Did this work in previous versions of io-ts?

Version 2.2.21 is affected, this is working in 2.2.20.

Software Version(s)
io-ts 2.2.21
fp-ts 2.16.2
io-ts-types 0.5.19
TypeScript 5.3.3
gcanti commented 7 months ago

The previous version of io-ts that I was using (2.2.20) did not have the same issue

That's because there was a bug that has been resolved. In your example:

const myRecord: Flags = {
  "1": true,
  "20": false,
};

the keys are not numbers ("1", "20" are strings), so they are stripped.

The issue arises from using NumberFromString as a key. You may consider using a different codec that merely validates whether a string is parseable as a number, without performing an actual transformation.

Wolfgang-stack commented 7 months ago

@gcanti thanks, that makes sense - I was thinking of NumberFromString the wrong way...

thanks for your reply