fix: refactor to flat generics with consistent names & patterns

This commit is contained in:
Tanner Linsley
2023-09-13 14:46:16 -06:00
parent 46b49ca669
commit 1fb28c53eb
15 changed files with 252 additions and 225 deletions

View File

@@ -6,7 +6,7 @@ import { useForm } from './useForm'
export type FormFactory<TFormData> = {
useForm: (opts?: FormOptions<TFormData>) => FormApi<TFormData>
useField: UseField<TFormData>
Field: FieldComponent<TFormData, TFormData>
Field: FieldComponent<TFormData>
}
export function createFormFactory<TFormData>(

View File

@@ -2,8 +2,8 @@ import type { FieldOptions, DeepKeys } from '@tanstack/form-core'
export type UseFieldOptions<
TData,
TFormData,
TName = unknown extends TFormData ? string : DeepKeys<TFormData>,
> = FieldOptions<TData, TFormData, TName> & {
TParentData,
TName extends DeepKeys<TParentData>,
> = FieldOptions<TData, TParentData, TName> & {
mode?: 'value' | 'array'
}

View File

@@ -1,9 +1,10 @@
import {
FieldApi,
type FieldApiOptions,
type FormApi,
import { FieldApi } from '@tanstack/form-core'
import type {
DeepKeys,
DeepValue,
Narrow,
ResolveData,
} from '@tanstack/form-core'
import type { DeepKeys, DeepValue, Narrow } from '@tanstack/form-core'
import { useStore } from '@tanstack/vue-store'
import { defineComponent, onMounted, onUnmounted, watch } from 'vue-demi'
import type { SlotsType, SetupContext, Ref } from 'vue-demi'
@@ -12,44 +13,52 @@ import type { UseFieldOptions } from './types'
declare module '@tanstack/form-core' {
// eslint-disable-next-line no-shadow
interface FieldApi<_TData, TFormData, Opts, TData> {
Field: FieldComponent<TFormData, TData>
interface FieldApi<
TData,
TParentData,
TName extends DeepKeys<TParentData>,
TResolvedData extends ResolveData<TData, TParentData, TName> = ResolveData<
TData,
TParentData,
TName
>,
> {
Field: FieldComponent<TResolvedData>
}
}
export type UseField<TFormData> = <TField extends DeepKeys<TFormData>>(
opts?: { name: Narrow<TField> } & UseFieldOptions<
DeepValue<TFormData, TField>,
TFormData
export type UseField<TParentData> = <TName extends DeepKeys<TParentData>>(
opts?: { name: Narrow<TName> } & UseFieldOptions<
DeepValue<TParentData, TName>,
TParentData,
TName
>,
) => FieldApi<DeepValue<TFormData, TField>, TFormData>
) => FieldApi<DeepValue<TParentData, TName>, TParentData, TName>
export function useField<
TData,
TFormData,
TName extends unknown extends TFormData
? string
: DeepKeys<TFormData> = unknown extends TFormData
? string
: DeepKeys<TFormData>,
TParentData,
TName extends DeepKeys<TParentData>,
>(
opts: UseFieldOptions<TData, TFormData, TName>,
opts: UseFieldOptions<TData, TParentData, TName>,
): {
api: FieldApi<
TData,
TFormData,
Omit<typeof opts, 'onMount'> & {
form: FormApi<TFormData>
}
TParentData,
TName
// Omit<typeof opts, 'onMount'> & {
// form: FormApi<TParentData>
// }
>
state: Readonly<
Ref<
FieldApi<
TData,
TFormData,
Omit<typeof opts, 'onMount'> & {
form: FormApi<TFormData>
}
TParentData,
TName
// Omit<typeof opts, 'onMount'> & {
// form: FormApi<TParentData>
// }
>['state']
>
>
@@ -91,17 +100,16 @@ export function useField<
return { api: fieldApi, state: fieldState } as never
}
export type FieldValue<TFormData, TField> = TFormData extends any[]
? unknown extends TField
? TFormData[number]
: DeepValue<TFormData[number], TField>
: DeepValue<TFormData, TField>
export type FieldValue<TParentData, TName> = TParentData extends any[]
? unknown extends TName
? TParentData[number]
: DeepValue<TParentData[number], TName>
: DeepValue<TParentData, TName>
type FieldComponentProps<
TData,
TParentData,
TFormData,
TField,
TName extends unknown extends TFormData ? string : DeepKeys<TFormData>,
TName extends DeepKeys<TParentData>,
> = (TParentData extends any[]
? {
name?: TName
@@ -111,40 +119,35 @@ type FieldComponentProps<
name: TName
index?: never
}) &
Omit<UseFieldOptions<TField, TFormData, TName>, 'name' | 'index'>
Omit<UseFieldOptions<TData, TParentData, TName>, 'name' | 'index'>
export type FieldComponent<TParentData, TFormData> = <
// Type of the field
TField,
// Name of the field
TName extends unknown extends TFormData ? string : DeepKeys<TFormData>,
export type FieldComponent<TParentData> = <
TData,
TName extends DeepKeys<TParentData>,
TResolvedData extends ResolveData<TData, TParentData, TName> = ResolveData<
TData,
TParentData,
TName
>,
>(
fieldOptions: FieldComponentProps<TParentData, TFormData, TField, TName>,
fieldOptions: FieldComponentProps<TData, TParentData, TName>,
context: SetupContext<
{},
SlotsType<{
default: {
field: FieldApi<
TField,
TFormData,
FieldApiOptions<TField, TFormData, TName>
>
state: FieldApi<
TField,
TFormData,
FieldApiOptions<TField, TFormData, TName>
>['state']
field: FieldApi<TData, TParentData, TName, TResolvedData>
state: FieldApi<TData, TParentData, TName, TResolvedData>['state']
}
}>
>,
) => any
export const Field = defineComponent(
<TData, TFormData>(
fieldOptions: UseFieldOptions<TData, TFormData>,
<TData, TParentData, TName extends DeepKeys<TParentData>>(
fieldOptions: UseFieldOptions<TData, TParentData, TName>,
context: SetupContext,
) => {
const fieldApi = useField({ ...fieldOptions, ...context.attrs })
const fieldApi = useField({ ...fieldOptions, ...context.attrs } as any)
provideFormContext({
formApi: fieldApi.api.form,

View File

@@ -14,7 +14,7 @@ declare module '@tanstack/form-core' {
interface FormApi<TFormData> {
Provider: (props: Record<string, any> & {}) => any
provideFormContext: () => void
Field: FieldComponent<TFormData, TFormData>
Field: FieldComponent<TFormData>
useField: UseField<TFormData>
useStore: <TSelected = NoInfer<FormState<TFormData>>>(
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,