From 420e86200a311e09405b90ab1759ed2086392d4a Mon Sep 17 00:00:00 2001 From: Henrik Volmer Date: Mon, 12 Jun 2023 18:52:17 +0200 Subject: [PATCH] fix(Yup resolver): schema type error (#563) * Fix schema type error mentioned in issue #549 * Improves yup type inference Improves yup type inference to make generic casting for useForm unnecessary --- yup/src/__tests__/Form.tsx | 8 ++++---- yup/src/__tests__/__fixtures__/data.ts | 7 ++++--- yup/src/index.ts | 1 - yup/src/types.ts | 24 ---------------------- yup/src/yup.ts | 28 +++++++++++++++++++++----- 5 files changed, 31 insertions(+), 37 deletions(-) delete mode 100644 yup/src/types.ts diff --git a/yup/src/__tests__/Form.tsx b/yup/src/__tests__/Form.tsx index b3ca809b..a3a822ae 100644 --- a/yup/src/__tests__/Form.tsx +++ b/yup/src/__tests__/Form.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import user from '@testing-library/user-event'; -import { useForm } from 'react-hook-form'; +import { SubmitHandler, useForm } from 'react-hook-form'; import * as Yup from 'yup'; import { yupResolver } from '..'; @@ -10,10 +10,10 @@ const schema = Yup.object({ password: Yup.string().required(), }); -type FormData = Yup.InferType & { unusedProperty: string }; +type FormData = Yup.InferType; interface Props { - onSubmit: (data: FormData) => void; + onSubmit: SubmitHandler; } function TestComponent({ onSubmit }: Props) { @@ -21,7 +21,7 @@ function TestComponent({ onSubmit }: Props) { register, formState: { errors }, handleSubmit, - } = useForm({ + } = useForm({ resolver: yupResolver(schema), // Useful to check TypeScript regressions }); diff --git a/yup/src/__tests__/__fixtures__/data.ts b/yup/src/__tests__/__fixtures__/data.ts index 39ccbc8e..90348fec 100644 --- a/yup/src/__tests__/__fixtures__/data.ts +++ b/yup/src/__tests__/__fixtures__/data.ts @@ -35,7 +35,7 @@ export const schemaWithWhen = yup.object({ }), }); -export const validData: yup.InferType = { +export const validData = { username: 'Doe', password: 'Password123_', repeatPassword: 'Password123_', @@ -50,14 +50,15 @@ export const validData: yup.InferType = { name: 'name', }, ], -}; +} satisfies yup.InferType; export const invalidData = { password: '___', email: '', birthYear: 'birthYear', like: [{ id: 'z' }], -}; + // Must be set to "unknown", otherwise typescript knows that it is invalid +} as unknown as Required>; export const fields: Record = { username: { diff --git a/yup/src/index.ts b/yup/src/index.ts index ea7705c8..13c89e6e 100644 --- a/yup/src/index.ts +++ b/yup/src/index.ts @@ -1,2 +1 @@ export * from './yup'; -export * from './types'; diff --git a/yup/src/types.ts b/yup/src/types.ts deleted file mode 100644 index 8bbc43dc..00000000 --- a/yup/src/types.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { FieldValues, ResolverOptions, ResolverResult } from 'react-hook-form'; -import * as Yup from 'yup'; - -type Options> = Parameters[1]; - -export type Resolver = >( - schema: T, - schemaOptions?: Options, - factoryOptions?: { - /** - * @default async - */ - mode?: 'async' | 'sync'; - /** - * Return the raw input values rather than the parsed values. - * @default false - */ - raw?: boolean; - }, -) => ( - values: TFieldValues, - context: TContext | undefined, - options: ResolverOptions, -) => Promise>; diff --git a/yup/src/yup.ts b/yup/src/yup.ts index df654884..3e2aee7d 100644 --- a/yup/src/yup.ts +++ b/yup/src/yup.ts @@ -1,7 +1,11 @@ import * as Yup from 'yup'; import { toNestError, validateFieldsNatively } from '@hookform/resolvers'; -import { appendErrors, FieldError } from 'react-hook-form'; -import { Resolver } from './types'; +import { + appendErrors, + FieldError, + FieldValues, + Resolver, +} from 'react-hook-form'; /** * Why `path!` ? because it could be `undefined` in some case @@ -38,9 +42,22 @@ const parseErrorSchema = ( ); }; -export const yupResolver: Resolver = - (schema, schemaOptions = {}, resolverOptions = {}) => - async (values, context, options) => { +export function yupResolver( + schema: Yup.ObjectSchema, + schemaOptions: Parameters<(typeof schema)['validate']>[1] = {}, + resolverOptions: { + /** + * @default async + */ + mode?: 'async' | 'sync'; + /** + * Return the raw input values rather than the parsed values. + * @default false + */ + raw?: boolean; + } = {}, +): Resolver { + return async (values, context, options) => { try { if (schemaOptions.context && process.env.NODE_ENV === 'development') { // eslint-disable-next-line no-console @@ -80,3 +97,4 @@ export const yupResolver: Resolver = }; } }; +}