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

Enhance client data type inference #8269

Merged
merged 11 commits into from
Dec 13, 2023

Conversation

brophdawg11
Copy link
Contributor

WIP for enhanced type inference for client data functions. We should be able to infer when it's a clientLoader/clientAction based on the types parameters and avoid deserializing if it doesn't return a Response...

Copy link

changeset-bot bot commented Dec 12, 2023

🦋 Changeset detected

Latest commit: b84bf03

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
@remix-run/react Patch
@remix-run/testing Patch
create-remix Patch
remix Patch
@remix-run/architect Patch
@remix-run/cloudflare Patch
@remix-run/cloudflare-pages Patch
@remix-run/cloudflare-workers Patch
@remix-run/css-bundle Patch
@remix-run/deno Patch
@remix-run/dev Patch
@remix-run/eslint-config Patch
@remix-run/express Patch
@remix-run/node Patch
@remix-run/serve Patch
@remix-run/server-runtime Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

? Awaited<ReturnType<T>>
: SerializeFrom<T>
: SerializeFrom<T>
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We return Awaited<ReturnType<T>> only when the generic is a function that doesn't return a Responseand has parameters of ClientLoaderFunctionArgs or ClientActionFunctionArgs

: Awaited<Output> // client naked object is just the object
: SerializeFromImpl<T>
: SerializeFromImpl<T>
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exporting this as part of SerializeFrom ensures we get this across the board in useLoaderData, useActionData, useFetcher, useRouteLoaderData, meta matches.data, etc.

Copy link
Contributor

@pcattori pcattori left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add test cases to the bottom of the serialize.ts file?

packages/remix-server-runtime/serialize.ts Outdated Show resolved Hide resolved
@@ -21,6 +21,9 @@ export type Jsonify<T> =
T extends Number ? number :
T extends Boolean ? boolean :

// Promises JSON.stringify to an empty object
T extends Promise<unknown> ? EmptyObject :
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2023-12-13 at 3 26 19 PM

Comment on lines +21 to +24
Parameters<T> extends [ClientLoaderFunctionArgs | ClientActionFunctionArgs] ?
// Client data functions may not serialize
SerializeClient<Awaited<Output>>
:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleaned it up so that this is all that's new and we don't touch the flow for existing loaders/actions

Comment on lines +32 to +57
// note: cannot be inlined as logic requires union distribution
// prettier-ignore
type SerializeClient<Output> =
Output extends TypedDeferredData<infer U> ?
// top-level promises
& {
[K in keyof U as K extends symbol
? never
: Promise<any> extends U[K]
? K
: never]: DeferValueClient<U[K]>; // use generic to distribute over union
}
// non-promises
& {
[K in keyof U as Promise<any> extends U[K] ? never : K]: U[K];
}
:
Output extends TypedResponse<infer U> ? Jsonify<U> :
Awaited<Output>

// prettier-ignore
type DeferValueClient<T> =
T extends undefined ? undefined :
T extends Promise<unknown> ? Promise<Awaited<T>> :
T;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copies of the Serialize/DeferValue types with Jsonify stuff removed (unless it's a TypedResponse and then we still call Jsonify)

| TypedResponse<T> // returned responses
| TypedResponse<never> // thrown responses
>;
type Loader<T> = () => Promise<TypedResponse<T>>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pcattori and I worked through some examples and we don't need to represent the never types since they don't show up in the signature if you throw conditionally.

@brophdawg11 brophdawg11 merged commit 2a6e63d into release-next Dec 13, 2023
5 checks passed
@brophdawg11 brophdawg11 deleted the brophdawg11/client-data-types branch December 13, 2023 21:06
Copy link
Contributor

🤖 Hello there,

We just published version 2.4.0-pre.9 which includes this pull request. If you'd like to take it for a test run please try it out and let us know what you think!

Thanks!

Copy link
Contributor

🤖 Hello there,

We just published version 2.4.0 which includes this pull request. If you'd like to take it for a test run please try it out and let us know what you think!

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants