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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hard to debug union types #5

Closed
wasd171 opened this issue Jun 26, 2017 · 15 comments
Closed

Hard to debug union types #5

wasd171 opened this issue Jun 26, 2017 · 15 comments

Comments

@wasd171
Copy link

wasd171 commented Jun 26, 2017

Hello,

I have created a webpackbin to illustrate the problem. If union is not used, the reporter shows the problem exactly, if not, it just prints the whole expected type and provided value.

Check the console on webpackbin to see what I mean

@OliverJAsh
Copy link
Collaborator

Hey @wasd171. Thanks for the issue.

If I understand correctly, instead of:

Expecting ({ foo: string } | null) but instead got: {"foo":17}.

You would like a more explicit error, like in your second example:

Expecting string at foo but instead got: 17.

I will have a look at the io-ts context we're getting back, but it may be that io-ts is not giving us enough detail on the context object here.

@wasd171
Copy link
Author

wasd171 commented Jun 29, 2017

Thank you.

@OliverJAsh
Copy link
Collaborator

We do seem to have the detail we need to achieve this level of reporting.

To achieve more specific error reporting, we would need to identify which type in the union has the closest match (in this case the InterfaceType), and then report against that type instead of the union.

I wonder how we could achieve this identification… 🤔 In your example, it would be quite simple because the value we're validating is an InterfaceTypeand the union we're validating against only has one InterfaceType, so that's the only candidate. However, I'm not sure what should happen in more complicated examples, e.g. a union with multiple interface types: { baz: string } | { foo: string, bar: string }—in this example there are two candidates, so we would need to match by property names somehow.

For reference, here is the value we get back from io-ts.

[
    {
        "value": {
            "foo": 17
        },
        "context": [
            {
                "key": "",
                "type": {
                    "_A": null,
                    "_tag": "UnionType",
                    "name": "({ foo: string } | null)",
                    "types": [
                        {
                            "_A": null,
                            "_tag": "InterfaceType",
                            "name": "{ foo: string }",
                            "props": {
                                "foo": {
                                    "_A": null,
                                    "_tag": "StringType",
                                    "name": "string"
                                }
                            }
                        },
                        {
                            "_A": null,
                            "_tag": "NullType",
                            "name": "null"
                        }
                    ]
                }
            }
        ]
    }
]

@wasd171
Copy link
Author

wasd171 commented Jun 29, 2017

I guess it's possible to just print the error for every type and let the user search.

@soerenBoisen
Copy link

I have the same problem with unions. I was hoping to use io-ts for helping debug the places where our TypeScript code interfaces with JavaScript projects, but our union types are producing errors that are just as hard to understand as the errors that happened without validation:

Expecting string at 0.meeting.attendees.0.displayName but instead got: undefined.
Expecting true at 1.hasOrder but instead got: false.
Expecting string at 1.meeting.attendees.0.displayName but instead got: undefined.
Expecting { hasResources: true, hasCatering: false, remoteOrderUid: string, meeting: { subject: string, meetingTime: { start: string, end: string }, attendees: Array<{ displayName: string, emailAddress: string, recipientType: (keyof ["user","distributionList","externalUser","other","unknown"]), appointmentResponse: (keyof ["none","organizer","tentative","accepted","declined","unknown"]), required: boolean }> }, resourceBooking: { resources: Array<{ id: number, name: string, resourceTypeId: number, serviceCenterId: number }> } } at 1.order.0 but instead got: undefined.
Expecting { hasResources: false, hasCatering: true, remoteOrderUid: string, meeting: { subject: string, meetingTime: { start: string, end: string }, attendees: Array<{ displayName: string, emailAddress: string, recipientType: (keyof ["user","distributionList","externalUser","other","unknown"]), appointmentResponse: (keyof ["none","organizer","tentative","accepted","declined","unknown"]), required: boolean }> }, cateringBooking: { deliveryTime: string, location: ({ type: "DELIVERY_TO_RESOURCE", resourceId: number } | { type: "FREE_TEXT_DELIVERY", location: string }), sittings: number, totalAmount: number, permaLink: string } } at 1.order.1 but instead got: undefined.
Expecting { hasResources: true, hasCatering: true, remoteOrderUid: string, meeting: { subject: string, meetingTime: { start: string, end: string }, attendees: Array<{ displayName: string, emailAddress: string, recipientType: (keyof ["user","distributionList","externalUser","other","unknown"]), appointmentResponse: (keyof ["none","organizer","tentative","accepted","declined","unknown"]), required: boolean }> }, resourceBooking: { resources: Array<{ id: number, name: string, resourceTypeId: number, serviceCenterId: number }> }, cateringBooking: { deliveryTime: string, location: ({ type: "DELIVERY_TO_RESOURCE", resourceId: number } | { type: "FREE_TEXT_DELIVERY", location: string }), sittings: number, totalAmount: number, permaLink: string } } at 1.order.2 but instead got: undefined.

The first line is the actual error - I am missing displayName of the attendee. But all the other errors are wrong, they are the result of it being a union type.

@gillchristian
Copy link
Owner

gillchristian commented Dec 16, 2019

@OliverJAsh

we would need to identify which type in the union has the closest match (in this case the InterfaceType), and then report against that type instead of the union.

That would be really great 🎉

Personally as long it returns one error instead of several it would be enough.

Eg. if a union is 'foo' | number | boolean | { bar: string } it error should look something like

Expecting one of 'foo', number, boolean or { bar: string } at some.path.0.to.the.value but instead got: undefined.

I know that already for a simple example the error is long but it could be multiline instead:

Expecting one of:
  'foo'
  number
  boolean 
  { bar: string }
at some.path.0.to.the.value but instead got: undefined.

This is already a good compromise IMO. But I don't know if it's hard/er to implement. I'm willing to give it a shot if you folks agree on it as a good (enough) solution.

@OliverJAsh
Copy link
Collaborator

@gillchristian That would definitely be an improvement!

@ktilcu
Copy link

ktilcu commented May 13, 2020

@OliverJAsh is this still a thing you'd wanna merge?

@gillchristian
Copy link
Owner

gillchristian commented May 13, 2020

Yes @ktilcu. I'm working (or was) on #17 which tries to improve reporting for union types.

I got stuck on transforming the io-ts context into error messages for more elaborate cases. So I need to get back to fixing that before being able to merge and release.

@osdiab
Copy link
Contributor

osdiab commented May 20, 2020

i'd like this too, so would love to help if I can!

@osdiab
Copy link
Contributor

osdiab commented Jun 3, 2020

@gillchristian now that that PR is in could this potentially be closed in favor of more specific enhancements? Also, do you have privs to publish an update to NPM? I just made my own user-scoped package to consume it for now.

@osdiab
Copy link
Contributor

osdiab commented Jun 3, 2020

cc @OliverJAsh

@gillchristian
Copy link
Owner

gillchristian commented Jun 3, 2020

do you have privs to publish an update to NPM?

No. I'll see if I can contact Oliver as he's not actively maintaining the project now (#17 (comment)).

now that that PR is in could this potentially be closed in favor of more specific enhancements?

Yes, I think that should be better 👌


Closed by #17

@gillchristian
Copy link
Owner

UPDATE: I do have access now to publishing on npm. But I have a problem to access my account, once that's sorted out I'll publish.

@osdiab

@gillchristian
Copy link
Owner

@osdiab sorry for the delay, I just published v1.1.0

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

6 participants