Skip to content

Update ErrorSchema type definition to better handle string #4553

@TheOneTheOnlyJJ

Description

@TheOneTheOnlyJJ

Prerequisites

What theme are you using?

core

Version

5.24.7

Current Behavior

Right now, I have the following useState:

const [extraErrors, setExtraErrors] = useState<ErrorSchema<IUserDataBoxConfigCreateInput> | undefined>(undefined);

where IUserDataBoxConfigCreateInput is:

interface IUserDataBoxConfigCreateInput {
  storageId: string;
  name: string;
  description?: string;
}

and I try to set the new state with the following function, to preserve any existing errors:

setExtraErrors((prevExtraErrors: ErrorSchema<IUserDataBoxConfigCreateInput> | undefined): ErrorSchema<IUserDataBoxConfigCreateInput> | undefined => {
  const NEW_ERROR = "Could not get username availability.";
  if (prevExtraErrors === undefined) {
    return {
      name: {
        __errors: [NEW_ERROR]
      } satisfies FieldErrors
    } satisfies ErrorSchema<IUserDataBoxConfigCreateInput>;
  }
  return {
    ...prevExtraErrors,
    name: {
       __errors: prevExtraErrors.name?.__errors === undefined ? [NEW_ERROR] : [...prevExtraErrors.name.__errors, NEW_ERROR]
    } satisfies FieldErrors
  } satisfies ErrorSchema<IUserDataBoxConfigCreateInput>;
});

Unfortunately, this yields a type error:

Type '{ __errors: string[]; }' is not assignable to type 'ErrorSchema<string>'.
  Type '{ __errors: string[]; }' is not assignable to type 'string'.ts(2322)
UserDataBoxConfigCreateInput.ts(8, 3): The expected type comes from property 'name' which is declared here on type 'ErrorSchema<IUserDataBoxConfigCreateInput>'
(property) name?: ErrorSchema<string> | undefined

After playing with the types and getting angry after not managing to fix it, I asked ChatGPT what's wrong with my code, also providing it with the ErrorSchema type definition from RJSF for context. And so, a surprising response was given:
"
When T = string, then ErrorSchema<string> resolves to:

FieldErrors & {
  [key in keyof string]?: ErrorSchema<T[key]>
}

But keyof string is not never!, it is actually string, number, Symbol, etc., because in TypeScript:

keyof string === keyof String === "length" | "toUpperCase" | ...

So ErrorSchema<string> ends up not just being FieldErrors, but some monster like:

{
  __errors?: string[];
  length?: ErrorSchema<...>;
  toUpperCase?: ErrorSchema<...>;
  ...
}

And then your code:

name: { __errors: [NEW_ERROR] } satisfies FieldErrors

Is not assignable to this complex monster structure.
"
While I am not exactly sure if this is the case or not, it also provided a solution:
"
Update ErrorSchema to only recurse on objects, like this:

export type ErrorSchema<T = any> =
  FieldErrors & (T extends object ? {
    [key in keyof T]?: ErrorSchema<T[key]>;
  } : {});

This says: "If T is an object (like a form field with properties), recurse. Otherwise, stop at FieldErrors."
This will now allow things like:

const errs: ErrorSchema<{ name: string }> = {
  name: {
    __errors: ["Nope"]
  }
};

And disallow ErrorSchema<string> from expecting keys like length, charAt, etc.
"
Sure enough, making the change to ErrorSchema locally fixed my error.

Expected Behavior

I expect no type error.

Steps To Reproduce

No response

Environment

Anything else?

This is the first time an LLM has fixed a library issue for me. I am impressed.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions