Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Surprising acceptance of various inputs #690

Open
NicoleRauch opened this issue Mar 9, 2023 · 1 comment
Open

Surprising acceptance of various inputs #690

NicoleRauch opened this issue Mar 9, 2023 · 1 comment

Comments

@NicoleRauch
Copy link

NicoleRauch commented Mar 9, 2023

馃悰 Bug report

Current Behavior

For educational purposes, I played around with the available codecs and encountered these (in my opinion erroneous) cases:

  1. t.UnknownArray accepts ["ABC", 123] which is a tuple, not an array.

  2. t.UnknownRecord accepts const n: number = 7; const x: Record<number, string> = {[n]: "n"}; although the documentation claims that it accepts only Record<string, unknown> i.e. string keys.

  3. t.record(t.string, t.number) accepts const n: number = 7; const x: Record<number, number> = {[n]: 888}; although the key is a number, not a string.

  4. t.strict({ a: t.number, b: t.string }) accepts {a: 777, b: "Hello", also: "x", and: 123} although it should reject objects with extra fields. The same holds true when t.strict is replaced by t.exact(t.type(...)).

  5. t.partial({a: t.string}) accepts {a: undefined} even when "exactOptionalPropertyTypes": true is set in tsconfig.json

Expected behavior

Ideally all of the above codecs should reject the presented example objects.

For 2) and 3) I am not sure whether these are really distinguishable types in TypeScript? (If I change the Record type to Record<string, ...> I don't get a typing error.) If they are not distinguishable, the documentation should reflect this.

Reproducible example

See above.

Suggested solution(s)

See above.

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.20
fp-ts 2.13.1
TypeScript 4.9.5
@NicoleRauch NicoleRauch changed the title Surprising acceptance of objects Surprising acceptance of various inputs Mar 9, 2023
@ragnese
Copy link

ragnese commented May 9, 2023

For 1, TypeScript tuple are arrays, in multiple senses. First, tuples are arrays in a literal sense in that JavaScript doesn't have "tuples". So, at runtime, anything that was a "tuple" in your TypeScript code really is a JavaScript Array at runtime. Second, even in TypeScript's type system, tuples are a subtype of Arrays. So, any function that accepts an array of a certain element type will also accept tuples of any arity as long as the elements in the tuple are compatible with the element type of the array in the signature.

For 2, this is also consistent with how JavaScript and TypeScript work (try passing a Record<number, string> to a function that accepts Record<string, string> and vice versa). Though, I suppose the documentation should be changed to not be misleading.

Same with 3. It's unfortunate, but JavaScript object keys are never really numbers. They always get converted to numbers. For example, this is valid: const a = ['a', 'b', 'c']['0'] // variable a now = 'a'. So, I don't think there's anything to do for this one, either.

4 is somewhat debatable, but I don't think it should work the way you're describing. TypeScript is primarily structurally typed, which means that types/interfaces describe a minimum requirement of a type. So, a TypeScript type will accept an object with "extra" fields as correct. The current io-ts behavior is consistent with that philosophy, and all strict/exact do is to actually strip those extra fields for us. It would be strange and inconsistent with the rest of TypeScript to reject objects with extra fields.

5 is the only one that I agree is troublesome. There are other issues that discuss io-ts's take on undefined vs. "optional" properties. The official stance of io-ts is/was that they will be treated the same. I disagree with this decision, but I'm just a user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants