react-jsonschema-form icon indicating copy to clipboard operation
react-jsonschema-form copied to clipboard

Typescript Examples don't work

Open hickscorp opened this issue 1 year ago • 11 comments

Scratching my original post - better start with something straight from the docs.

EDIT: Straight from the docs, and using latest versions of everything:

import Form from "@rjsf/core";
import { customizeValidator } from "@rjsf/validator-ajv8";
import { TabTransition } from "../../components/layout/TabTransitions";
import { JSONSchemaType } from "ajv";

interface FormData {
  foo: string;
  bar: number;
}

type Schema = JSONSchemaType<FormData>;

const schema: Schema = {
  type: "object",
  properties: {
    foo: { type: "string" },
    bar: { type: "number" },
  },
  required: ["foo", "bar"],
};

export const Edit = () => {
  const formData: FormData = {
    foo: "",
    bar: 0,
  };

  const validator = customizeValidator<FormData>();

  return (
    <TabTransition>
      <Form<FormData, Schema> schema={schema} validator={validator} formData={formData} />
    </TabTransition>
  );
};

The line <Form<FormData, Schema> schema={schema} validator={validator} formData={formData} /> fails with:

Type 'Schema' does not satisfy the constraint 'JSONSchema7'.
  Type '{ anyOf: readonly UncheckedJSONSchemaType<FormData, false>[]; } & { [keyword: string]: any; $id?: string | undefined; $ref?: string | undefined; $defs?: Record<...> | undefined; definitions?: Record<...> | undefined; }' is not assignable to type 'JSONSchema7'.
    Types of property '$defs' are incompatible.
      Type 'Record<string, UncheckedJSONSchemaType<Known, true>> | undefined' is not assignable to type '{ [key: string]: JSONSchema7Definition; } | undefined'.
        Type 'Record<string, UncheckedJSONSchemaType<Known, true>>' is not assignable to type '{ [key: string]: JSONSchema7Definition; }'.
          'string' index signatures are incompatible.
            Type 'UncheckedJSONSchemaType<Known, true>' is not assignable to type 'JSONSchema7Definition'.
              Type '{ anyOf: readonly UncheckedJSONSchemaType<Known, true>[]; } & { [keyword: string]: any; $id?: string | undefined; $ref?: string | undefined; $defs?: Record<string, UncheckedJSONSchemaType<...>> | undefined; definitions?: Record<...> | undefined; }' is not assignable to type 'JSONSchema7Definition'.
                Type '{ anyOf: readonly UncheckedJSONSchemaType<Known, true>[]; } & { [keyword: string]: any; $id?: string | undefined; $ref?: string | undefined; $defs?: Record<string, UncheckedJSONSchemaType<...>> | undefined; definitions?: Record<...> | undefined; }' is not assignable to type 'JSONSchema7'.
                  Types of property 'anyOf' are incompatible.
                    The type 'readonly UncheckedJSONSchemaType<Known, true>[]' is 'readonly' and cannot be assigned to the mutable type 'JSONSchema7Definition[]'.

Ok. Let's remove the generic hints so that it reads <Form schema={schema} validator={validator} formData={formData} />. Error:

Type '{ type: "object"; additionalProperties?: boolean | UncheckedJSONSchemaType<unknown, false> | undefined; unevaluatedProperties?: boolean | UncheckedJSONSchemaType<unknown, false> | undefined; ... 7 more ...; maxProperties?: number | undefined; } & { ...; } & { ...; } & { ...; }' is not assignable to type 'JSONSchema7'.
  Types of property '$defs' are incompatible.
    Type 'Record<string, UncheckedJSONSchemaType<Known, true>> | undefined' is not assignable to type '{ [key: string]: JSONSchema7Definition; } | undefined'.
      Type 'Record<string, UncheckedJSONSchemaType<Known, true>>' is not assignable to type '{ [key: string]: JSONSchema7Definition; }'.
        'string' index signatures are incompatible.
          Type 'UncheckedJSONSchemaType<Known, true>' is not assignable to type 'JSONSchema7Definition'.
            Type '{ anyOf: readonly UncheckedJSONSchemaType<Known, true>[]; } & { [keyword: string]: any; $id?: string | undefined; $ref?: string | undefined; $defs?: Record<string, UncheckedJSONSchemaType<...>> | undefined; definitions?: Record<...> | undefined; }' is not assignable to type 'JSONSchema7Definition'.
              Type '{ anyOf: readonly UncheckedJSONSchemaType<Known, true>[]; } & { [keyword: string]: any; $id?: string | undefined; $ref?: string | undefined; $defs?: Record<string, UncheckedJSONSchemaType<...>> | undefined; definitions?: Record<...> | undefined; }' is not assignable to type 'JSONSchema7'.
                Types of property 'anyOf' are incompatible.
                  The type 'readonly UncheckedJSONSchemaType<Known, true>[]' is 'readonly' and cannot be assigned to the mutable type 'JSONSchema7Definition[]'.

Even removing all other props (In case there are mismatches with inference) such as <Form schema={schema} /> yields the same error.

The only way I found to "fix" this is to use <Form<FormData, JSONSchema7> schema={schema as JSONSchema7} validator={validator} formData={formData} />. But we would lose most of the types in the process...

hickscorp avatar Apr 17 '23 16:04 hickscorp

@hickscorp So it looks like you are running into the fact that we have defined StrictRJSFSchema as the json-schema provided type. Since you are using the AJV generic type instead, what happens if you use a global type augmentation to enhance the StrictRJSFSchema type to support JSONSchemaType<any>? We hesitate to force support for the AJV generic type in our base implementation at this point since we want our utilities to be validator implementation independent.

heath-freenome avatar Apr 21 '23 15:04 heath-freenome

@heath-freenome thanks, but I truly have no clue what you're asking me to do. How could I do this?

hickscorp avatar May 08 '23 22:05 hickscorp

I think the problem we have is fairly clear by now that we've been struggling for a bit trying to leverage Typescript with RJSF without success.

This doesn't work:

      <Form<FormData>
        validator={validator}
        liveValidate
        noHtml5Validate
        formData={formData}
        schema={schema}
        uiSchema={uiSchema}
        fields={fields}
        templates={{ ButtonTemplates }}
        widgets={widgets}
        onSubmit={onSubmit}
        showErrorList="bottom"
        disabled={isLoading}
      />

We have to force a cast, and we're loosing all the typings down the line:

      <Form<FormData>
        validator={validator}
        liveValidate
        noHtml5Validate
        formData={formData}
        schema={schema as JSONSchema7}
        uiSchema={uiSchema}
        fields={fields}
        templates={{ ButtonTemplates }}
        widgets={widgets}
        onSubmit={onSubmit}
        showErrorList="bottom"
        disabled={isLoading}
      />

hickscorp avatar May 08 '23 22:05 hickscorp

@hickscorp Sorry for the delay, missed your previous message. At what level are you needing the typing to be maintained? Do you have custom widgets, fields or templates?

heath-freenome avatar May 09 '23 20:05 heath-freenome

@heath-freenome Thanks (once more :D) We do have custom fields, widgets and templates - yes. Ideally we would like to understand how to properly type the Form component - not downtype it to JSONSchema7 or a supertype of our own schema. We would expect that this way, we would get warnings at compile time on invalid custom widget typings etc.

hickscorp avatar May 11 '23 14:05 hickscorp

@hickscorp Given that you are choosing to use the AJV 8 typing scheme, I'm not sure how to work around your situation except to force the typing. I've created this issue in the ajv validator to get them to extract their types into a separate repo so that we can support AJV types without being forced to pull in ALL of ajv into our utilities. Perhaps putting some weight into AJV issues that are blocking you/us may help it happen?

heath-freenome avatar May 11 '23 18:05 heath-freenome

Just wanted to second this issue as we are in a similar situation. We use custom widgets, templates and would like to everything to be typed, the formData, schema, etc. It might be helpful to at least provide working Typescript examples in the documentation.

zbayoff avatar Jul 21 '23 13:07 zbayoff

@zbayoff Can you help us and respond to the AJV issue?

heath-freenome avatar Jul 21 '23 16:07 heath-freenome

Any updates on this issue? The current example in the Documentation does not work. The issue above ^ does not seem to have been addressed by AJV, and I'm not sure relying on them to split their repo will happen anytime soon. Is there an alternate way/package to achieve typed json form data and schemas?

zbayoff avatar Feb 29 '24 09:02 zbayoff

@zbayoff I wish there was a simpler way to do this that didn't create a HARD link between RJSF utils and AJV. Aside from someone in this community doing the work and submitting it to AJV, we are at their mercy here. In all honesty, RJSF is still stuck on JSON Schema v7. The only other option is for us to CLONE the AJV types locally and then have to manually keep everything in sync (PAINFUL). Given that, are you willing to build a new package for json-schema-types that steals their types and also support the JSONSchema7 type as well?

heath-freenome avatar Mar 01 '24 21:03 heath-freenome

This is a sad situation to be in. It's a real shame because their JsonSchemaType is much more powerful than the RJSF one.

TheOneTheOnlyJJ avatar Aug 15 '24 10:08 TheOneTheOnlyJJ