diff --git a/.changeset/olive-pumpkins-kick.md b/.changeset/olive-pumpkins-kick.md new file mode 100644 index 00000000000..0c42a921230 --- /dev/null +++ b/.changeset/olive-pumpkins-kick.md @@ -0,0 +1,6 @@ +--- +"remix": patch +"@remix-run/react": patch +--- + +support undefined unions as optional keys in `UseDataFunctionReturn` type diff --git a/contributors.yml b/contributors.yml index e4f12650499..d1929c13f71 100644 --- a/contributors.yml +++ b/contributors.yml @@ -153,6 +153,7 @@ - isaacrmoreno - ishan-me - IshanKBG +- itsMapleLeaf - jacob-ebey - JacobParis - jakewtaylor diff --git a/packages/remix-react/__tests__/hook-types-test.tsx b/packages/remix-react/__tests__/hook-types-test.tsx index 8e7d998f8fa..acd2c96b0f4 100644 --- a/packages/remix-react/__tests__/hook-types-test.tsx +++ b/packages/remix-react/__tests__/hook-types-test.tsx @@ -100,4 +100,14 @@ describe("type serializer", () => { type response = UseDataFunctionReturn; isEqual(true); }); + + it("makes keys optional if the value is undefined", () => { + type AppData = { + arg1: string; + arg2: number | undefined; + arg3: undefined; + }; + type response = UseDataFunctionReturn; + isEqual(true); + }); }); diff --git a/packages/remix-react/components.tsx b/packages/remix-react/components.tsx index c702a514eb6..faf4592d241 100644 --- a/packages/remix-react/components.tsx +++ b/packages/remix-react/components.tsx @@ -20,6 +20,7 @@ import { useResolvedPath, } from "react-router-dom"; import type { LinkProps, NavLinkProps } from "react-router-dom"; +import type { Merge } from "type-fest"; import type { AppData, FormEncType, FormMethod } from "./data"; import type { EntryContext, AssetsManifest } from "./entry"; @@ -1336,6 +1337,7 @@ type JsonPrimitives = | Boolean | null; type NonJsonPrimitives = undefined | Function | symbol; + type SerializeType = T extends JsonPrimitives ? T : T extends NonJsonPrimitives @@ -1353,13 +1355,24 @@ type SerializeType = T extends JsonPrimitives : T extends (infer U)[] ? (U extends NonJsonPrimitives ? null : SerializeType)[] : T extends object - ? { - [k in keyof T as T[k] extends NonJsonPrimitives - ? never - : k]: SerializeType; - } + ? SerializeObject> : never; +type SerializeObject = { + [k in keyof T as T[k] extends NonJsonPrimitives ? never : k]: SerializeType< + T[k] + >; +}; + +type UndefinedOptionals = Merge< + { + [k in keyof T as undefined extends T[k] ? never : k]: NonNullable; + }, + { + [k in keyof T as undefined extends T[k] ? k : never]?: NonNullable; + } +>; + export type UseDataFunctionReturn = T extends ( ...args: any[] ) => infer Output diff --git a/packages/remix-react/package.json b/packages/remix-react/package.json index 3c136aa7354..6a7609f1220 100644 --- a/packages/remix-react/package.json +++ b/packages/remix-react/package.json @@ -17,7 +17,8 @@ "module": "dist/esm/index.js", "dependencies": { "history": "^5.3.0", - "react-router-dom": "^6.2.2" + "react-router-dom": "^6.2.2", + "type-fest": "^2.17.0" }, "devDependencies": { "@testing-library/jest-dom": "^5.16.2", diff --git a/yarn.lock b/yarn.lock index e775d57ae5a..ba30b90380a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12127,6 +12127,11 @@ type-fest@^2.16.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.16.0.tgz#1250fbd64dafaf4c8e405e393ef3fb16d9651db2" integrity sha512-qpaThT2HQkFb83gMOrdKVsfCN7LKxP26Yq+smPzY1FqoHRjqmjqHXA7n5Gkxi8efirtbeEUxzfEdePthQWCuHw== +type-fest@^2.17.0: + version "2.17.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.17.0.tgz#c677030ce61e5be0c90c077d52571eb73c506ea9" + integrity sha512-U+g3/JVXnOki1kLSc+xZGPRll3Ah9u2VIG6Sn9iH9YX6UkPERmt6O/0fIyTgsd2/whV0+gAaHAg8fz6sG1QzMA== + type-is@^1.6.18, type-is@~1.6.18: version "1.6.18" resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz"