Skip to content

Commit

Permalink
Merge pull request #40 from iway1/bug/create-unique-field-typings
Browse files Browse the repository at this point in the history
fix typing issues with create unique field schema
  • Loading branch information
iway1 authored Jan 14, 2023
2 parents 006513e + 6e5f818 commit 5489cca
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 14 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ coverage
.yarn
.vscode
lib
.env
.env

src/playground.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from "react";
import { z } from "zod";
import {
addHiddenProperties,
Expand Down Expand Up @@ -30,6 +31,38 @@ describe("createFieldSchema", () => {
createTsForm(mapping);
}).not.toThrowError();
});
it("should correctly type the form with multiple unique field schemas.", () => {
const A = createUniqueFieldSchema(z.string(), "one");
const B = createUniqueFieldSchema(z.string(), "two");
function In1(_: { req: string }) {
return <div />;
}
function In2(_: { req2: string }) {
return <div />;
}
const mapping = [
[A, In1],
[B, In2],
] as const;

const Form = createTsForm(mapping);

<Form
schema={z.object({
a: A,
b: B,
})}
onSubmit={() => {}}
props={{
a: {
req: "One",
},
b: {
req2: "Two",
},
}}
/>;
});
});

describe("addHiddenProperties", () => {
Expand Down
34 changes: 33 additions & 1 deletion src/__tests__/createSchemaForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
useReqDescription,
useTsController,
} from "../FieldContext";
import { createUniqueFieldSchema } from "../createFieldSchema";

const testIds = {
textField: "_text-field",
Expand Down Expand Up @@ -766,5 +767,36 @@ describe("createSchemaForm", () => {
render(<Form schema={Schema} onSubmit={() => {}} />);
}).toThrow();
});
it("should be possible to show nested error messages with the 'errorMessage' helper returned from `useTsController`", () => {});
it("should correctly type the form with multiple unique field schemas.", () => {
const A = createUniqueFieldSchema(z.string(), "one");
const B = createUniqueFieldSchema(z.string(), "two");
function In1(_: { req: string }) {
return <div />;
}
function In2(_: { req2: string }) {
return <div />;
}
const mapping = [
[A, In1],
[B, In2],
] as const;

const Form = createTsForm(mapping);

<Form
schema={z.object({
a: A,
b: B,
})}
onSubmit={() => {}}
props={{
a: {
req: "One",
},
b: {
req2: "Two",
},
}}
/>;
});
});
15 changes: 9 additions & 6 deletions src/createFieldSchema.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { z, ZodBranded } from "zod";
import { RTFSupportedZodTypes } from "./supportedZodTypes";

export const HIDDEN_ID_PROPERTY = "_rtf_id";
Expand All @@ -21,14 +22,14 @@ export function isSchemaWithHiddenProperties<T extends RTFSupportedZodTypes>(
return HIDDEN_ID_PROPERTY in schemaType;
}

export function addHiddenProperties<T extends RTFSupportedZodTypes>(
schema: T,
properties: HiddenProperties
) {
export function addHiddenProperties<
ID extends string,
T extends RTFSupportedZodTypes
>(schema: T, properties: HiddenProperties) {
for (const key in properties) {
(schema as any)[key] = properties[key as keyof typeof properties];
}
return schema;
return schema as ZodBranded<T, ID>;
}

export function duplicateIdErrorMessage(id: string) {
Expand Down Expand Up @@ -59,5 +60,7 @@ export function createUniqueFieldSchema<
Identifier extends string
>(schema: T, id: Identifier) {
const r = schema.brand<Identifier>();
return addHiddenProperties(r, { [HIDDEN_ID_PROPERTY]: id });
return addHiddenProperties<Identifier, typeof r>(r, {
[HIDDEN_ID_PROPERTY]: id,
}) as z.ZodBranded<T, Identifier>;
}
17 changes: 11 additions & 6 deletions src/createSchemaForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ import { DeepPartial, useForm, UseFormReturn } from "react-hook-form";
import { AnyZodObject, z, ZodEffects } from "zod";
import { getComponentForZodType } from "./getComponentForZodType";
import { zodResolver } from "@hookform/resolvers/zod";
import { IndexOf, RequireKeysWithRequiredChildren } from "./typeUtilities";
import {
IndexOf,
IndexOfUnwrapZodType,
RequireKeysWithRequiredChildren,
UnwrapMapping,
} from "./typeUtilities";
import { getMetaInformationForZodType } from "./getMetaInformationForZodType";
import { unwrapEffects, UnwrapZodType } from "./unwrap";
import { unwrapEffects } from "./unwrap";
import { RTFBaseZodType, RTFSupportedZodTypes } from "./supportedZodTypes";
import { FieldContextProvider } from "./FieldContext";
import { isZodTypeEqual } from "./isZodTypeEqual";
Expand Down Expand Up @@ -292,9 +297,9 @@ export function createTsForm<
props?: RequireKeysWithRequiredChildren<
Partial<{
[key in keyof z.infer<SchemaType>]: Mapping[IndexOf<
Mapping,
UnwrapMapping<Mapping>,
readonly [
UnwrapZodType<
IndexOfUnwrapZodType<
ReturnType<UnwrapEffects<SchemaType>["_def"]["shape"]>[key]
>,
any
Expand All @@ -303,9 +308,9 @@ export function createTsForm<
? Omit<
ComponentProps<
Mapping[IndexOf<
Mapping,
UnwrapMapping<Mapping>,
readonly [
UnwrapZodType<
IndexOfUnwrapZodType<
ReturnType<
UnwrapEffects<SchemaType>["_def"]["shape"]
>[key]
Expand Down
30 changes: 30 additions & 0 deletions src/typeUtilities.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { z } from "zod";
import type { FormComponentMapping } from "./createSchemaForm";
import { RTFBaseZodType, RTFSupportedZodTypes } from "./supportedZodTypes";
import { UnwrapZodType } from "./unwrap";

/**
* @internal
*/
Expand Down Expand Up @@ -61,6 +66,31 @@ export type Indexes<V extends readonly any[]> = {
[K in Exclude<keyof V, keyof Array<any>>]: K;
};

/**
* @internal
*/
export type UnwrapZodBrand<T extends RTFBaseZodType> = T extends z.ZodBranded<
z.ZodTypeAny,
infer ID
>
? ID
: T;

/**
* @internal
*/
export type UnwrapMapping<T extends FormComponentMapping> = {
[Index in keyof T]: T[Index] extends readonly [any, any]
? readonly [UnwrapZodBrand<T[Index][0]>, any]
: never;
};

/**
* @internal
*/
export type IndexOfUnwrapZodType<T extends RTFSupportedZodTypes> =
T extends z.ZodBranded<z.ZodTypeAny, infer ID> ? ID : UnwrapZodType<T>;

/**
* @internal
*/
Expand Down

0 comments on commit 5489cca

Please sign in to comment.