Hello, When we use a schema with a non-symmetrical transformation like this one
const Schema = S.Struct({
createdAt: S.DateFromString
})
We have a problem because react-hook-form and the resolver infer this schema as
{ readonly createdAt: Date }
const form = useForm({
resolver: effectTsResolver(Schema),
defaultValues: {
createAt: "",
// ^ Type 'string' is not assignable to type 'Date'.ts(2322)
},
});
And this is more problematic when we try to use this field
<FormField
control={form.control}
name="createAt"
render={({ field }) => {
return (
<FormItem>
<FormControl>
<Input {...field} />
{*/ ^ Type '{ onChange: (...event: any[]) => void; onBlur: Noop; value: Date; disabled?: boolean | undefined; name: "createAt"; ref: RefCallBack; }' is not assignable to type 'InputProps'. /*}
</FormControl>
</FormItem>
);
}}
/>
Yes because, at this moment, the input value type should be a string not a Date.
So the current implementation requires us to only use symmetrical schema with filters:
like:
const Schema = S.Struct{{
createdAt: S.String.pipe(
MyStringIsValidDateFilter()
)
}}
and then, in the handleSubmit
handler:
onSubmit={handleSubmit(data => {
//. ^ { created: string}
const date = new Date(data);
})}
this looks redundant because we already have a way to express we want a Date from a string.
So i've looked at the source, we have a notion of TTransformedValue
. It's not very documented but it looks that this is the type after they are transformed by the validator.
So I switched the types of the resolver to match this:
export type Resolver = <A extends FieldValues, I extends FieldValues, TContext>(
schema: Schema.Schema<A, I>,
config?: ParseOptions,
) => (
values: I,
_context: TContext | undefined,
options: ResolverOptions<I>,
) => Promise<ResolverResult<I>>;
instead of
export type Resolver = <A extends FieldValues, I, TContext>(
schema: Schema.Schema<A, I>,
config?: ParseOptions,
) => (
values: A,
_context: TContext | undefined,
options: ResolverOptions<A>,
) => Promise<ResolverResult<A>>;
I have the good types :
type To = S.Schema.Type<typeof Schema>;
type From = S.Schema.Encoded<typeof Schema>;
export function MyForm() {
const form = useForm<From, any, To>({ // to type correctly here
resolver: effectTsResolver(Schema),
defaultValues: {
createAt: "",
// ^ yeahh
},
});
return (
<div>
<Form {...form}>
<form onSubmit={form.handleSubmit((data) => console.log(data))}>
// ^ yeah it's a Date !!!!
<FormField
control={form.control}
name="createAt"
render={({ field }) => {
return (
<FormItem>
<FormControl>
<Input {...field} type="datetime-local" />
// works here too
</FormControl>
</FormItem>
);
}}
/>
</form>
</Form>
</div>
);
}
This is an example for Date but it's very powerful when we encode missing or bad information into Option
for example. Like with: OptionFromUndefinedOr
.
Soo, if you are agree with that change, I can write a PR and change the example in the readme of react-hook form.
Curious to have your feedbacks !!!