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’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix wrong behaviour with respect to TypeScript's type system #9
Conversation
Another option (which I like much more) is to prune unknown properties instead of failing. Example const Payload = t.interface({
a: t.string,
b: t.number
})
const bigPayload = { a: 's', b: 1, c: true /* other props here...*/ }
// the validation here doesn't fail and the unknown properties are removed
// hence the result honours the contract
t.validate(bigPayload, Payload).map(p => console.log(p)) // => { a: 's', b: 1 } This would allow for partial descriptions of APIs with big payloads. A candidate implementation is on branch |
@gcanti any plans regarding merge estimation?
I do really need this feature as well. Working right now with legacy APIs and most of the data make no sense to me, so it feels inefficient and misleading to write types for all the pieces. |
@gyzerok In order to quickly merge one of these PRs I'd love some reviews / feedback. In particular there are 3 possible choices with respect to unknown props
(and then
(there are a couple of other PRs for the 0.1 milestone) |
t.instanceOfWell, not really can say something here. I do not have any use case where I need to check for instance of. t.object -> t.interfaceThis renaming makes a lot of sense to me and I'd say that it's probably best way to rename it out of all possible ones. t.mapping -> t.indexHere it seems like new one and old one are both not really sensible. Just by looking at both names I can't clearly understand what the thing do. Maybe something like Unknown props behaviourOut of all possible options here I'd say that pruning unknown props is better one:
|
I like it (also is the name I'm already using in tcomb)
It depends. Flow by default allows for additional props and you can turn on strictness with the TypeScript seems the other way around: strict by default const x: { name: string } = { name: 'Giulio', a: 1 } // error: Object literal may only specify known properties, and 'a' does not exist in type '{ name: string; }'. and then you can loose the type const x: { name: string, [key: string]: any } = { name: 'Giulio', a: 1 } // no error (btw I vote for pruning as well) |
I guess in TypeScript they are not strict in the same sense as in Flow. They behave more like extensible records: "object has at least these fields". Not even sure I can imagine use case for "at most" variation :) |
To summarize, by default
so const x: { name: string } = { name: 'Giulio', a: 1 } is not an error in Flow but is an error in TypeScript. Hence the current behaviour (v0.0.3) is not correct, either we must check for excess properties and fail or prune them. |
Ah, you are right, I kind of messed type definitions and just distructuring in mind. Anyway prune is cool :) |
Hi @gcanti I'm sorry for late response but I had very busy days...
And finally about the Unknown props I don't like the the fail strict-check behaviour, I prefer to simply ignore those props and maybe keep it there (just copying it), or eventually prune it but based on a precise user intention: let's say having a |
@dmorosinotto according to what we discussed around record strictness in TypeScript it might be misleading to copy props, because in this case type signature will contradict underlying data. I would propose to make only Also I guess it's a bit off-topic to this PR, but I would say that library responsibility goes over just validation. I'd probably call it more decoding. From this perspective pruning doesn't feel anyhow wrong or unexpected. I'll open an issue with better explanation regarding validation/decoding later, so we can discuss it separately. |
Fine. Closing in favour of #20 |
@gyzerok Interesting, please do. In the meanwhile just an observation: (value: any, context: Context) => Either<Array<ValidationError>, T>; It says that you will find a Here an example: deserializing an ISO string to a const DateFromISOString = new t.Type<Date>(
'Date',
(v, c) => {
const d = new Date(v)
return isNaN(d.getTime()) ? t.failure<Date>(v, c) : t.success(d)
}
)
console.log(t.validate('foo', DateFromISOString)) // => Left(...)
console.log(t.validate('2017-02-14T14:24:39.446Z', DateFromISOString)) // => Right(Date(2017, 01, 14)) |
Yes thats exactly what I am talking about. The way you can use library goes a bit beyond validation. And I'd personally say it is useful feature. I do already have date parsing in a way similar to your example in my code as well as a bit advanced number parsing (for example for Actually I've started using the library because I wanted something close to Elm decoders. For me it doesn't matter much if you work with raw JSON or parsed one, it was more about being able to achieve type safety. I will write about decoding and overall experience soon, just need to find a bit of time to give thoughts a good shape. |
@gyzerok just a precisation with respect to my statement above
It holds only for Object literals const y = { name: 'Giulio', a: 1 }
const x: { name: string } = { name: 'Giulio', a: 1 } // error
const z: { name: string } = y // NO error |
This PR aims to fix some current wrong behaviours with respect to TypeScript's type system (feedback much appreciated)
New Feature
Breaking Changes
t.Object
type. Renamed tot.Index
, now accepts arrays so is fully equivalent to{ [key: string]: any }
.t.instanceOf
combinator. Removed.t.object
/t.record
combinator. Renamed tot.interface
.ObjectType
/RecordType
toInterfaceType
. Excess properties are now checked:mapping
combinator. Renamed toindex
.MappingType
toIndexType
.intersection
combinator. Due to the new excess property checks int.interface
now only acceptInterfaceType
s./cc @dmorosinotto