kiliman / remix-typedjson

This package is a replacement for superjson to use in your Remix app. It handles a subset of types that `superjson` supports, but is faster and smaller.
MIT License
435 stars 19 forks source link

Add type definition for the new V2_Meta Remix API #26

Open piotrkulpinski opened 1 year ago

piotrkulpinski commented 1 year ago

Hi, Love the project! Thanks for working on this.

I just started implementing some of the new Remix APIs that are coming in 2.0 and typescript is throwing errors with the TypedMetaFunction as it's using the old V1_HtmlMetaDescriptor return type.

Any chance to add V2_TypedMetaFunction to fix that?

Thanks!

nickluger commented 11 months ago

This is a working workaround:

import type {
  V2_ServerRuntimeMetaArgs,
  V2_ServerRuntimeMetaDescriptor,
} from "@remix-run/server-runtime";
import type { UseDataFunctionReturn } from "remix-typedjson";

export type V2TypedMetaFunction<Loader> = (
  args: Omit<V2_ServerRuntimeMetaArgs, "data"> & {
    data: UseDataFunctionReturn<Loader> | undefined;
  }
) => V2_ServerRuntimeMetaDescriptor[];

export const meta: VV2TypedMetaFunction<typeof loader> = ({ data }) => {}

--- EDIT ---

Obviously, this package does not touch the de-serialization handled magically by Remix for the meta function, so for the meta function we don't have access to the types provided by remix-typedjson.

The correct type thus would be:

import type {
  DataFunctionArgs,
  SerializeFrom,
  V2_ServerRuntimeMetaArgs,
  V2_ServerRuntimeMetaDescriptor,
} from "@remix-run/server-runtime";
import type { TypedJsonResponse } from "remix-typedjson";

export type V2TypedMetaFunction<
  Loader extends (args: DataFunctionArgs) => Promise<TypedJsonResponse>
> = (
  args: Omit<V2_ServerRuntimeMetaArgs, "data"> & {
    data: SerializeFrom<PromiseReturnType<PromiseReturnType<Loader>["typedjson"]>> | undefined;
  }
) => V2_ServerRuntimeMetaDescriptor[];

// helper
export type PromiseReturnType<T extends (...arguments_: any) => Promise<any>> = PromiseType<
  ReturnType<T>
>;

which uses the SerializeFrom type provided by Remix

paul-vd commented 3 months ago

Since this has been released in remix, there is still no alternative for remix-typedjson is this still planned?

kiliman commented 3 months ago

Although I do plan to deprecate this package in favor of Single Fetch in Remix, I may be able to come up with a workaround.

k1sul1 commented 3 months ago

Sooo... I'm curious. How exactly did TypedMetaFunction not work?

export const meta: TypedMetaFunction<typeof loader> = ({ data }) => {
  let page = data?.job.title || 'Job not found';

  return [{ title: `${page} | Portal` }];
};

This seems to work. In this case the value is a plain string. Anything that requires deserialization probably won't work though?

I did find an interesting case; data.job is inferred as Job, as the loader in question either throws or returns a Job. However, in reality, if the user hits a 404, data is undefined. Is this one of the ways it doesn't work? 😁

kiliman commented 3 months ago

Unfortunately, the types "lied" to you. Sure your types look right, but the data was still the original JSON, and not deserialized by typedjson. So your native types like Date would still be strings even though the type said they were Date.

This was because there was no extension point in Remix for me to add the deserialization, unlike useTypedLoaderData, where I can do the conversion from useLoaderData before returning.

I'll create a helper function that wraps your meta so you get the converted types.