Open NicoleRauch opened 1 year ago
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.
🐛 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
acceptsconst n: number = 7; const x: Record<number, string> = {[n]: "n"};
although the documentation claims that it accepts onlyRecord<string, unknown>
i.e. string keys.3)
t.record(t.string, t.number)
acceptsconst 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 whent.strict
is replaced byt.exact(t.type(...))
.5)
t.partial({a: t.string})
accepts{a: undefined}
even when"exactOptionalPropertyTypes": true
is set in tsconfig.jsonExpected 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?