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

Can't create JTDSchemaType from tagged union with no additional properties #2123

Closed
jkav77 opened this issue Oct 9, 2022 · 3 comments · Fixed by #2158
Closed

Can't create JTDSchemaType from tagged union with no additional properties #2123

jkav77 opened this issue Oct 9, 2022 · 3 comments · Fixed by #2158

Comments

@jkav77
Copy link

jkav77 commented Oct 9, 2022

What I'm trying to do
I want to use the JTDSchemaType to create a schema for messages I will pass through a websocket. The messages have a type field used with a discriminator in the schema. Some of the messages require no additional information, such as a "ping" message, but I am getting an error when creating the JTDSchemaType unless there are additional properties.

What version of Ajv are you using? Does the issue happen if you use the latest version?
v8.11

Ajv options object

{}

Your code

import Ajv, { JTDSchemaType } from "ajv/dist/jtd";

const ajv = new Ajv();

type Message = { type: "ping" } // causes error unless an additional property is added
            |  { type: "error"; message: string };

const schema: JTDSchemaType<Message> = {
  discriminator: "type",
  mapping: {
    ping: {
      properties: {}, // compile error because ping is type 'never'
    },
    error: {
      properties: {
        message: { type: "string" },
      },
    },
  },
};

What results did you expect?
I expect to be able to create a schema for a tagged union that has no properties except the tag. I can't compile this because typescript expects the mapping of ping to be type never.

Are you going to resolve the issue?
I'm not sure how, but I'm happy to help.

@epoberezkin
Copy link
Member

cc @erikbrinkman :)

erikbrinkman added a commit to erikbrinkman/ajv that referenced this issue Nov 14, 2022
erikbrinkman added a commit to erikbrinkman/ajv that referenced this issue Nov 14, 2022
@erikbrinkman
Copy link
Collaborator

Thanks for finding this! Every time there's a bug I heave a sigh because it's partially caused by typescripts weird type system. In general typescript types are permissive, meaning { prop: type } is a narrowing of the object enforcing that prop has type but the object could also have any other member set as well. This means that {} actually allows all objects as a type.

The only exception to this rule is with object literals, which can't specify extra members. That means when creating these schema types, since we're using them mainly for object literals, we're actually bending the type system behave in a way it doesn't normally.

For discriminators we remove the discriminator field from the definition with an Omit, but if there are no other properties this creates the empty object type {}. Ideally this would naturally fit a record, but most of the rules don't work since {} has the reverse assignability as one might expect.

There may be clever ways to handle this better, but the PR I submitted #2158 just special cases that object, since it's already tricky enough to work with..

erikbrinkman added a commit to erikbrinkman/ajv that referenced this issue Nov 14, 2022
@jkav77
Copy link
Author

jkav77 commented Nov 27, 2022

Thanks for explaining. Now I understand why it wasn't working before. I will keep track of the pull request so I can remove my hackish workaround when it gets merged.

epoberezkin pushed a commit that referenced this issue Jan 2, 2023
@epoberezkin epoberezkin added this to the 8.12 milestone Jan 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

4 participants
@erikbrinkman @epoberezkin @jkav77 and others