You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
record.is does not agree with the TypeScript Record type in the case of "partially enumerable" records, i.e., records whose domain is the disjoint union of some enumerable type and some non-enumerable type.
import*astfrom'io-ts';import*asEfrom'fp-ts/Either';constPrefixed=t.refinement(// I use refinement to avoid needing to decode all my string literal keys belowt.string,(s): s is `prefix:${string}` =>s.startsWith('prefix:'),'`prefix:${string}`');constPartiallyEnumerableRecord=t.record(t.union([t.literal('a'),Prefixed]),t.string);typePartiallyEnumerableRecord=t.TypeOf<typeofPartiallyEnumerableRecord>;// According to TypeScript, a PartiallyEnumerableRecord _must_ include property `a`typeMissingEnumerableKeys={};constmissingANotAssignableToPartiallyEnumerableRecord: MissingEnumerableKeysextendsPartiallyEnumerableRecord
? true
: false=false;typeHasEnumerableKeys={a: 'a'};consthasANotAssignableToPartiallyEnumerableRecord: HasEnumerableKeysextendsPartiallyEnumerableRecord
? true
: false=true;constmissingEnumerableKeys: MissingEnumerableKeys={};// However, `io-ts` does not require an object to include property `a` to pass as `PartiallyEnumerableRecord.is`console.log(`${JSON.stringify(missingEnumerableKeys)}${PartiallyEnumerableRecord.is(missingEnumerableKeys) ? 'is' : 'is not'}${PartiallyEnumerableRecord.name}`);console.log(`${JSON.stringify(missingEnumerableKeys)}${E.isRight(PartiallyEnumerableRecord.decode(missingEnumerableKeys)) ? 'is' : 'is not'}${PartiallyEnumerableRecord.name}`);
Suggested solution(s)
getDomainKeys needs to be made more flexible, to support tracking both the enumerable and non-enumerable parts of a type: currently, any non-enumerable subtype of the domain causes record to choose nonEnumerableRecord. With this extra information, if disjoint enumerable and non-enumerable parts are found, record can choose to use an intersection to combine the corresponding enumerableRecord and nonEnumerableRecord. This disjointness is important, since otherwise the enumerable portion will be subsumed into the non-enumerable portion: 'a' | string === string.
#707 is a draft of what I think this could look like. If this all sounds reasonable, I will finish cleaning it up and completing test coverage.
Additional context
Your environment
Which versions of io-ts are affected by this issue? Did this work in previous versions of io-ts?
As far as I know this has never worked as expected.
Exact versions are also available in the linked StackBlitz environment.
Software
Version(s)
io-ts
2.2.21
fp-ts
2.16.1
TypeScript
5.3.2
The text was updated successfully, but these errors were encountered:
🐛 Bug report
Current Behavior
record.is
does not agree with the TypeScriptRecord
type in the case of "partially enumerable" records, i.e., records whose domain is the disjoint union of some enumerable type and some non-enumerable type.In the following example (source code is also copy-pasted under Reproducible example) — https://stackblitz.com/edit/node-42t6hm?file=index.ts — this behavior is unexpected:
Expected behavior
In the above example, I expect
since
missingEnumerableKeys
is not assignable toPartiallyEnumerableRecord
at the type-level.Reproducible example
https://stackblitz.com/edit/node-42t6hm?file=index.ts
Suggested solution(s)
getDomainKeys
needs to be made more flexible, to support tracking both the enumerable and non-enumerable parts of a type: currently, any non-enumerable subtype of thedomain
causesrecord
to choosenonEnumerableRecord
. With this extra information, if disjoint enumerable and non-enumerable parts are found,record
can choose to use anintersection
to combine the correspondingenumerableRecord
andnonEnumerableRecord
. This disjointness is important, since otherwise the enumerable portion will be subsumed into the non-enumerable portion:'a' | string === string
.#707 is a draft of what I think this could look like. If this all sounds reasonable, I will finish cleaning it up and completing test coverage.
Additional context
Your environment
Which versions of io-ts are affected by this issue? Did this work in previous versions of io-ts?
As far as I know this has never worked as expected.
Exact versions are also available in the linked StackBlitz environment.
The text was updated successfully, but these errors were encountered: