From dcb80495ffdefb2e789887e1d40b2c4a57ade257 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Sat, 2 Jan 2021 23:28:28 +0200 Subject: [PATCH] feat: enhance useField types --- docs/content/api/use-field.md | 18 ++++++++++++ packages/vee-validate/src/useField.ts | 42 +++++++++++++++------------ 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/docs/content/api/use-field.md b/docs/content/api/use-field.md index 837bad56c..b49c5818d 100644 --- a/docs/content/api/use-field.md +++ b/docs/content/api/use-field.md @@ -58,6 +58,24 @@ export default { You are responsible for when the field validates, blurs or when its value changes. This gives you greater control over the `Field` component which may include or implement sensible defaults for most common use cases. +## Usage with TypeScript + +You can use `useField` with typescript and type the field's value type to ensure safety when manipulating it's value. The `useField` function is a generic function that receives the value type and applies it on the various interactions you have with its API. + +```typescript +const { value, resetField } = useField('email', yup.string().email()); + +value.value = 1; // ⛔️ Error +value.value = 'test@example.com'; // ✅ + +resetField({ + value: 1, // ⛔️ Error +}); +resetField({ + value: 'test@example.com', // ✅ +}); +``` + ## API Reference The full signature of the `useField` function looks like this: diff --git a/packages/vee-validate/src/useField.ts b/packages/vee-validate/src/useField.ts index 0f1db128e..6326a052f 100644 --- a/packages/vee-validate/src/useField.ts +++ b/packages/vee-validate/src/useField.ts @@ -26,19 +26,19 @@ import { import { isCallable } from '../../shared'; import { FieldContextSymbol, FormInitialValuesSymbol, FormContextSymbol } from './symbols'; -interface FieldOptions { - initialValue: any; +interface FieldOptions { + initialValue: TValue; validateOnValueUpdate: boolean; validateOnMount?: boolean; bails?: boolean; type?: string; - valueProp?: MaybeReactive; - uncheckedValue?: MaybeReactive; + valueProp?: MaybeReactive; + uncheckedValue?: MaybeReactive; label?: MaybeReactive; } -interface FieldState { - value: any; +interface FieldState { + value: TValue; dirty: boolean; touched: boolean; errors: string[]; @@ -51,7 +51,11 @@ let ID_COUNTER = 0; /** * Creates a field composite. */ -export function useField(name: MaybeReactive, rules?: RuleExpression, opts?: Partial) { +export function useField( + name: MaybeReactive, + rules?: RuleExpression, + opts?: Partial> +) { const fid = ID_COUNTER >= Number.MAX_SAFE_INTEGER ? 0 : ++ID_COUNTER; const { initialValue, @@ -74,7 +78,7 @@ export function useField(name: MaybeReactive, rules?: RuleExpression, op setValidationState, value, checked, - } = useValidationState({ + } = useValidationState({ name, // make sure to unref initial value because of possible refs passed in initValue: unref(initialValue), @@ -146,7 +150,7 @@ export function useField(name: MaybeReactive, rules?: RuleExpression, op watchValue(); - function resetField(state?: Partial) { + function resetField(state?: Partial>) { unwatchValue?.(); resetValidationState(state); watchValue(); @@ -155,7 +159,7 @@ export function useField(name: MaybeReactive, rules?: RuleExpression, op const field = { fid, name, - value: value, + value, meta, errors, errorMessage, @@ -175,7 +179,7 @@ export function useField(name: MaybeReactive, rules?: RuleExpression, op setDirty, }; - provide(FieldContextSymbol, field); + provide(FieldContextSymbol, field as any); if (isRef(rules) && typeof unref(rules) !== 'function') { watch(rules, validate, { @@ -257,7 +261,7 @@ function normalizeOptions(name: string, opts: Partial | undefined) /** * Manages the validation state of a field. */ -function useValidationState({ +function useValidationState({ name, initValue, form, @@ -265,14 +269,14 @@ function useValidationState({ valueProp, }: { name: MaybeReactive; - initValue?: any; + initValue?: TValue; form?: FormContext; type?: string; valueProp: any; }) { const errors: Ref = ref([]); const formInitialValues = injectWithSelf(FormInitialValuesSymbol, undefined); - const initialValue = getFromPath(unref(formInitialValues), unref(name)) ?? initValue; + const initialValue: TValue = getFromPath(unref(formInitialValues), unref(name)) ?? initValue; const { resetMeta, meta } = useMeta(initialValue); const value = useFieldValue(initialValue, name, form); if (hasCheckedAttr(type) && initialValue) { @@ -322,7 +326,7 @@ function useValidationState({ } // Resets the validation state - function resetValidationState(state?: Partial) { + function resetValidationState(state?: Partial>) { const fieldPath = unref(name); const newValue = state && 'value' in state ? state.value : getFromPath(unref(formInitialValues), fieldPath) ?? initValue; @@ -365,7 +369,7 @@ function useMeta(initialValue: any) { /** * Resets the flag state */ - function resetMeta(state?: Pick, 'dirty' | 'touched' | 'value'>) { + function resetMeta(state?: Pick>, 'dirty' | 'touched' | 'value'>) { const defaults = initialMeta(); meta.pending = defaults.pending; meta.touched = state?.touched ?? defaults.touched; @@ -395,7 +399,7 @@ function extractRuleFromSchema(schema: Record | undefined, fieldNam /** * Manages the field value */ -function useFieldValue(initialValue: any, path: MaybeReactive, form?: FormContext) { +function useFieldValue(initialValue: TValue, path: MaybeReactive, form?: FormContext) { // if no form is associated, use a regular ref. if (!form) { return ref(initialValue); @@ -404,11 +408,11 @@ function useFieldValue(initialValue: any, path: MaybeReactive, form?: Fo // set initial value setInPath(form.values, unref(path), initialValue); // otherwise use a computed setter that triggers the `setFieldValue` - const value = computed({ + const value = computed({ get() { return getFromPath(form.values, unref(path)); }, - set(newVal: any) { + set(newVal) { form.setFieldValue(unref(path), newVal); }, });