Skip to content

Commit

Permalink
feat: enhance useField types
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Jan 2, 2021
1 parent 641f26c commit dcb8049
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 19 deletions.
18 changes: 18 additions & 0 deletions docs/content/api/use-field.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>('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:
Expand Down
42 changes: 23 additions & 19 deletions packages/vee-validate/src/useField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,19 @@ import {
import { isCallable } from '../../shared';
import { FieldContextSymbol, FormInitialValuesSymbol, FormContextSymbol } from './symbols';

interface FieldOptions {
initialValue: any;
interface FieldOptions<TValue = any> {
initialValue: TValue;
validateOnValueUpdate: boolean;
validateOnMount?: boolean;
bails?: boolean;
type?: string;
valueProp?: MaybeReactive<any>;
uncheckedValue?: MaybeReactive<any>;
valueProp?: MaybeReactive<TValue>;
uncheckedValue?: MaybeReactive<TValue>;
label?: MaybeReactive<string>;
}

interface FieldState {
value: any;
interface FieldState<TValue = any> {
value: TValue;
dirty: boolean;
touched: boolean;
errors: string[];
Expand All @@ -51,7 +51,11 @@ let ID_COUNTER = 0;
/**
* Creates a field composite.
*/
export function useField(name: MaybeReactive<string>, rules?: RuleExpression, opts?: Partial<FieldOptions>) {
export function useField<TValue = any>(
name: MaybeReactive<string>,
rules?: RuleExpression,
opts?: Partial<FieldOptions<TValue>>
) {
const fid = ID_COUNTER >= Number.MAX_SAFE_INTEGER ? 0 : ++ID_COUNTER;
const {
initialValue,
Expand All @@ -74,7 +78,7 @@ export function useField(name: MaybeReactive<string>, rules?: RuleExpression, op
setValidationState,
value,
checked,
} = useValidationState({
} = useValidationState<TValue>({
name,
// make sure to unref initial value because of possible refs passed in
initValue: unref(initialValue),
Expand Down Expand Up @@ -146,7 +150,7 @@ export function useField(name: MaybeReactive<string>, rules?: RuleExpression, op

watchValue();

function resetField(state?: Partial<FieldState>) {
function resetField(state?: Partial<FieldState<TValue>>) {
unwatchValue?.();
resetValidationState(state);
watchValue();
Expand All @@ -155,7 +159,7 @@ export function useField(name: MaybeReactive<string>, rules?: RuleExpression, op
const field = {
fid,
name,
value: value,
value,
meta,
errors,
errorMessage,
Expand All @@ -175,7 +179,7 @@ export function useField(name: MaybeReactive<string>, rules?: RuleExpression, op
setDirty,
};

provide(FieldContextSymbol, field);
provide(FieldContextSymbol, field as any);

if (isRef(rules) && typeof unref(rules) !== 'function') {
watch(rules, validate, {
Expand Down Expand Up @@ -257,22 +261,22 @@ function normalizeOptions(name: string, opts: Partial<FieldOptions> | undefined)
/**
* Manages the validation state of a field.
*/
function useValidationState({
function useValidationState<TValue = any>({
name,
initValue,
form,
type,
valueProp,
}: {
name: MaybeReactive<string>;
initValue?: any;
initValue?: TValue;
form?: FormContext;
type?: string;
valueProp: any;
}) {
const errors: Ref<string[]> = 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) {
Expand Down Expand Up @@ -322,7 +326,7 @@ function useValidationState({
}

// Resets the validation state
function resetValidationState(state?: Partial<FieldState>) {
function resetValidationState(state?: Partial<FieldState<TValue>>) {
const fieldPath = unref(name);
const newValue =
state && 'value' in state ? state.value : getFromPath(unref(formInitialValues), fieldPath) ?? initValue;
Expand Down Expand Up @@ -365,7 +369,7 @@ function useMeta(initialValue: any) {
/**
* Resets the flag state
*/
function resetMeta(state?: Pick<Partial<FieldState>, 'dirty' | 'touched' | 'value'>) {
function resetMeta<TValue = any>(state?: Pick<Partial<FieldState<TValue>>, 'dirty' | 'touched' | 'value'>) {
const defaults = initialMeta();
meta.pending = defaults.pending;
meta.touched = state?.touched ?? defaults.touched;
Expand Down Expand Up @@ -395,7 +399,7 @@ function extractRuleFromSchema(schema: Record<string, any> | undefined, fieldNam
/**
* Manages the field value
*/
function useFieldValue(initialValue: any, path: MaybeReactive<string>, form?: FormContext) {
function useFieldValue<TValue>(initialValue: TValue, path: MaybeReactive<string>, form?: FormContext) {
// if no form is associated, use a regular ref.
if (!form) {
return ref(initialValue);
Expand All @@ -404,11 +408,11 @@ function useFieldValue(initialValue: any, path: MaybeReactive<string>, 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<TValue>({
get() {
return getFromPath(form.values, unref(path));
},
set(newVal: any) {
set(newVal) {
form.setFieldValue(unref(path), newVal);
},
});
Expand Down

0 comments on commit dcb8049

Please sign in to comment.