diff --git a/.changeset/nervous-walls-sing.md b/.changeset/nervous-walls-sing.md new file mode 100644 index 00000000000..96ba17a3aef --- /dev/null +++ b/.changeset/nervous-walls-sing.md @@ -0,0 +1,10 @@ +--- +"remix": patch +"@remix-run/react": patch +--- + +Fix inferred types for `useLoaderData` and `useActionData` to preserve `null`s. + +Previously, `null`s were being replaced by `never`s due to usage of `NonNullable` in `UndefinedOptionals`. +Properties that aren't unions with `undefined` are now kept as-is, while properties that _do_ include `undefined` +are still made optional, but _only_ remove `undefined` from the property type whereas `NonNullable` also removed `null`s. diff --git a/packages/remix-react/components.tsx b/packages/remix-react/components.tsx index 0c3f7eae6a0..3adb0c6546b 100644 --- a/packages/remix-react/components.tsx +++ b/packages/remix-react/components.tsx @@ -1364,12 +1364,20 @@ type SerializeObject = { >; }; -type UndefinedOptionals = Merge< +/* + * For an object T, if it has any properties that are a union with `undefined`, + * make those into optional properties instead. + * + * Example: { a: string | undefined} --> { a?: string} + */ +type UndefinedOptionals = Merge< { - [k in keyof T as undefined extends T[k] ? never : k]: NonNullable; + // Property is not a union with `undefined`, keep as-is + [k in keyof T as undefined extends T[k] ? never : k]: T[k]; }, { - [k in keyof T as undefined extends T[k] ? k : never]?: NonNullable; + // Property _is_ a union with `defined`. Set as optional (via `?`) and remove `undefined` from the union + [k in keyof T as undefined extends T[k] ? k : never]?: Exclude; } >;