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

Bad decode result when using union and undefined #677

Open noshiro-pf opened 1 year ago

noshiro-pf commented 1 year ago

🐛 Bug report

Current Behavior & Expected behavior & Reproducible example

import * as t from 'io-ts';

const A = t.type({
  A: t.union([t.number, t.undefined, t.null]),
});

const B = t.type({
  B: t.union([t.number, t.undefined, t.null]),
});

const C = t.partial({
  C: t.union([t.number, t.null]),
});

const UnionAB = t.union([A, B]);
const UnionBA = t.union([B, A]);
const UnionAC = t.union([A, C]);
const UnionCA = t.union([C, A]);

const target = { A: 1 };

{
  const res = UnionAB.decode(target);
  if (isRight(res)) {
    console.log(res.right); // { A: 1 }  <- ok
    console.log(A.is(res.right)); // true  <- ok
    console.log(B.is(res.right)); // false  <- ok
  }
}

{
  const res = UnionBA.decode(target);
  if (isRight(res)) {
    console.log(res.right); // { A: 1, B: undefined }  <- NG (expected to be { A: 1 })
    console.log(A.is(res.right)); // true  <- ok
    console.log(B.is(res.right)); // true  <- NG (expected to be false)
  }
}

{
  const res = UnionCA.decode(target);
  if (isRight(res)) {
    console.log(res.right); // { A: 1 }  <- NG (expected to be {})
    console.log(A.is(res.right)); // true  <- ok
    console.log(C.is(res.right)); // true  <- ok
  }
}

{
  const res = UnionAC.decode(target);
  if (isRight(res)) {
    console.log(res.right); // { A: 1 }
    console.log(A.is(res.right)); // true  <- ok
    console.log(C.is(res.right)); // true  <- ok
  }
}

Suggested solution(s)

I think io-ts should distinguish between t.partial({ a: t.number }) and t.type({ a: t.union([t.number, t.undefined]) }) by using Object.hasOwnProperty in addition to checking if the key's value is undefined.

Additional context

Your environment

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

Software Version(s)
io-ts 2.2.16
fp-ts 2.11.8
TypeScript 4.5.5