#useActionData type inference does not work

1 messages ยท Page 1 of 1 (latest)

trim pewter
#
export async function action({ request }: ActionArgs) {
  const formData = await request.formData();
  const parsing = z
    .object({ ... })
    .safeParse(Object.fromEntries(formData.entries()));

  if (!parsing.success) {
    const errors = parsing.error.flatten();
    return json({ errors }, { status: 400 });
  }

  try {
    ...
  } catch (error) {
    if (
      error instanceof Prisma.PrismaClientKnownRequestError &&
      error.code === "P2002"
    ) {
      return json({ error: "This name is already taken" }, { status: 400 });
    }
    throw error;
  }
}

export default function AdminCreateOrganisation() {
  const data = useActionData<typeof action>();

  const error = data?.error;
  const errors = data?.errors?.fieldErrors;

Leads to:

app/routes/admin/organisations/create.tsx:50:23 - error TS2339: Property 'error' does not exist on type 'SerializeObject<UndefinedToOptional<{ errors: typeToFlattenedError<{ name: string; id: string; }, string>; }>> | SerializeObject<...>'.
  Property 'error' does not exist on type 'SerializeObject<UndefinedToOptional<{ errors: typeToFlattenedError<{ name: string; id: string; }, string>; }>>'.

50   const error = data?.error;
                         ~~~~~

app/routes/admin/organisations/create.tsx:51:24 - error TS2339: Property 'errors' does not exist on type 'SerializeObject<UndefinedToOptional<{ errors: typeToFlattenedError<{ name: string; id: string; }, string>; }>> | SerializeObject<...>'.
  Property 'errors' does not exist on type 'SerializeObject<UndefinedToOptional<{ error: string; }>>'.

51   const errors = data?.errors?.fieldErrors;
                          ~~~~~~
#

Hello! I feel that I already faced that issue, but I can't figure out how to fix that...

#

if I return these instead:

    return json({ errors, error: null }, { status: 400 });
    ...
    return json(
      { errors: null, error: "This name is already taken" },
      { status: 400 }
    );

It works. However, it seems wrong to have to specify null values. What if I need to return 100 json({...}) with different formats? It is not sustainable to repeat null fields everywhere.

#

There must be another way. Perhaps I'm doing something wrong.

#

Obviously the goal is to use the error(s) to augment the UI with relevant feedbacks, so I doubt throwing is the answer.

muted linden
#

I think you'll need to do error in data or errors in data first.

trim pewter
#
  const error = "error" in data ? data?.error : null;

gives me 'data' is possibly 'undefined'

#
const error = "error" in (data ?? {}) ? data?.error : null;

works but it smells dirty ^^'

muted linden
#

data && 'error' in data

trim pewter
#

hum ok ;D

#
const error = data && "error" in data && data.error;
muted linden
#

Yea.

trim pewter
#

is that really the way?

#

or should the typing be fixed (meaning should I open an issue) ? might be that's not possible to fix that

muted linden
#

Yea. You could return a discriminated union too.

return json({ type: 'error', error })

return json({ type: 'errors', errors })

const error = actionData.type === 'error' ? actionData.error : undefined
trim pewter
#
  const errors =
    data &&
    "errors" in data &&
    "fieldErrors" in data.errors &&
    data.errors.fieldErrors;

this one is failing on 'data.errors' is possibly 'null'.

trim pewter
#

Well, doesn't seem to like it:

const error = data?.type === "error" ? data.error : undefined;
Property 'error' does not exist on type 'SerializeObject<UndefinedToOptional<{ type: string; errors: typeToFlattenedError<{ id: string; name: string; }, string>; }>> | SerializeObject<...>'.
#

@sly dirge sorry for the ping, but I believe you are the expert for this ๐Ÿ™

muted linden
trim pewter
#

mmmh even if that works (which I'm sure it does), I feel this is too smelly to be the end solution here ร”_o

#

if I have 5, 10, x attributes, I'm not going to list them in that manner.

#

Perhaps I miss something that is obvious, or I must live with that. But that definitely seems strange.

sly dirge
# trim pewter mmmh even if that works (which I'm sure it does), I feel this is too smelly to b...

Unfortunately, this is just how TS works. Check out my conversation with Ryan Cavanaugh for more details (https://twitter.com/pcattori/status/1598359344827056131). You'll need to do some type narrowing by either using discriminated unions (i.e. use a type field to distinguish the different payloads) or do what @muted linden said and use 'field' in data checks.

We could add some normalization of the different JSON return values, but that would be actively trying to do more than what TS does for us, so not 100% convinced of that yet.

My recommendation is to use discriminated union via type field.

Disclaimer: I didn't read this thread in detail since its New Years Eve ๐Ÿ˜† so I'm making some assumptions. But hopefully this helps. If not, feel free to tag me again and I'll chime in

trim pewter
#

thank you both, and happy new year ๐Ÿ˜‰