remix
remix copied to clipboard
Type mismatch when returning from useLoaderData
Reproduction
When strictNullChecks is disabled, if all fields within a field are optional, then the parent field itself becomes optional. This is due to wrapping the returned result in a JsonifyObject. Sorry for my bad English.
type Result = {
foo: {bar?: string}
};
export const loader = () => {
const foo: Result = {foo: {bar: '123'}}
return json(foo)
}
export default function Component() {
const data = useLoaderData<typeof loader>()
const foo: Result = data;
}
Type 'JsonifyObject<Result>' is not assignable to type 'Result'.
Property 'foo' is optional in type 'JsonifyObject<Result>' but required in type 'Result'.
System Info
-
Used Package Manager
npm
Expected Behavior
types are the same
Actual Behavior
types don't match
Experiencing the same issues.
i generally have to do return json<Result>(...)
to get types to work right
It's one of the reasons I created remix-typedjson
. It exposes the native TypeScript types and converts the JSON data back to native types automatically.
https://github.com/kiliman/remix-typedjson
@kiliman great that you've created the repo, I'll check it out.
But I think this is a core Remix issue, isn't it?
@iampeter I think any framework that serializes to JSON from server to client will have this issue. I believe once Remix supports RSC, they'll be able to stream native types to the client, and this will no longer be an issue.
@iampeter I think any framework that serializes to JSON from server to client will have this issue. I believe once Remix supports RSC, they'll be able to stream native types to the client, and this will no longer be an issue.
we are not talking about serialization of types, but about what type useLoaderData returns.
it works correctly
type ExtractGeneric<Type> = Type extends TypedResponse<infer X> ? X : never
export function useDataFromLoader<T extends LoaderFunction>() {
return useLoaderData() as ExtractGeneric<Awaited<ReturnType<T>>>
}
import { useLoaderData } from "@remix-run/react"
export const useCustomLoaderData = <T extends (...args: any) => any>() => {
return useLoaderData() as Awaited<ReturnType<T>>
}