TanStack Form 的 PreactFormExtendedApi 类型别名:FormApi 与 Preact 专属能力的类型级融合

📅 发布时间:2026/9/17 8:13:02
TanStack Form 的 PreactFormExtendedApi 类型别名:FormApi 与 Preact 专属能力的类型级融合
TanStack Form 的 PreactFormExtendedApi 类型别名FormApi 与 Preact 专属能力的类型级融合【免费下载链接】form Headless, performant, and type-safe form state management for TS/JS, React, Vue, Angular, Solid, and Lit.项目地址: https://gitcode.com/GitHub_Trending/form/form导读PreactFormExtendedApi是 TanStack Form 在 Preact 适配层tanstack/preact-form中定义的核心类型别名它把框架无关的FormApi类实例与 Preact 专属的Field、FormGroup、Subscribe等能力合并为同一类型。本文将以该类型别名为核心解析它的定义结构、12 个泛型参数、底层源码实现并结合官方 Quick Start 与仓库示例说明它在实际表单开发中如何提供端到端的类型安全。一、类型别名概述一次交叉类型的“能力合并”PreactFormExtendedApi定义于 packages/preact-form/src/useForm.tsx其完整定义如下type PreactFormExtendedApiTFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta FormApiTFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta PreactFormApiTFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta;这是一个典型的 TypeScript交叉类型Intersection Type左侧的FormApi...来自框架无关的核心包tanstack/form-core提供表单状态管理、校验调度、提交处理等全部底层能力右侧的PreactFormApi...定义于 packages/preact-form/src/useForm.tsx是 Preact 适配层“追加”到FormApi上的专属成员。从源码结构看可以推断该类型别名的设计意图是在不破坏核心FormApi类型的前提下通过交叉类型把 Preact 特有的渲染与订阅能力“附加”到表单实例上从而让useForm的返回值同时具备核心逻辑方法与 Preact 组件式 API。二、12 个泛型参数逐一解读该类型别名接受 12 个泛型参数其中前 11 个约束了表单校验/提交回调的类型最后 1 个承载提交元数据。它们与核心包中FormApi、FormOptions的泛型保持严格一致参见 docs/framework/preact/reference/functions/useForm.md。泛型参数约束含义TFormData无约束表单数据的形状即defaultValues的推断类型TOnMountundefined \| FormValidateOrFnTFormData表单挂载时同步校验函数TOnChangeundefined \| FormValidateOrFnTFormData表单值变化时同步校验函数TOnChangeAsyncundefined \| FormAsyncValidateOrFnTFormData表单值变化时异步校验函数TOnBlurundefined \| FormValidateOrFnTFormData失焦时同步校验函数TOnBlurAsyncundefined \| FormAsyncValidateOrFnTFormData失焦时异步校验函数TOnSubmitundefined \| FormValidateOrFnTFormData提交时同步校验函数TOnSubmitAsyncundefined \| FormAsyncValidateOrFnTFormData提交时异步校验函数TOnDynamicundefined \| FormValidateOrFnTFormData动态添加/删除字段时同步校验函数TOnDynamicAsyncundefined \| FormAsyncValidateOrFnTFormData动态添加/删除字段时异步校验函数TOnServerundefined \| FormAsyncValidateOrFnTFormData服务端校验函数用于 Next.js/Remix 等服务端提交场景TSubmitMeta无约束提交时携带的附加元数据类型其中FormValidateOrFnTFormData指同步校验函数或标准 SchemaFormAsyncValidateOrFnTFormData指返回 Promise 的异步校验函数或标准 Schema。所有on*校验回调均允许为undefined这与useForm可缺省传入opts的设计一致packages/preact-form/src/useForm.tsx 中opts为可选参数。值得注意在PreactFormApi接口定义packages/preact-form/src/useForm.tsx中这些泛型均声明为in out双向变型这允许类型在“读取表单状态”与“写入校验回调”两个方向上都保持严格的类型安全推断。三、PreactFormApiPreact 专属的三大能力PreactFormApi接口完整文档见 docs/framework/preact/reference/interfaces/PreactFormApi.md为该类型别名注入了三个成员1.Field—— 渲染并管理单个字段Field: FieldComponentTFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta;它是一个 Preact 组件用于渲染并管理表单中的单个字段。FieldComponent类型定义于 packages/preact-form/src/useField.tsx接收children与一系列fieldOptions如name、validators并通过children渲染函数暴露字段级FieldApi。2.FormGroup—— 字段组的组合与复用FormGroup: FormGroupComponentTFormData, ...;用于将多个字段组合为一个可复用的逻辑分组是 TanStack Form 字段组Field Group功能在 Preact 层的接入点。3.Subscribe—— 订阅表单状态并触发副作用Subscribe: TSelected(props: { selector?: (state: FormState...) TSelected; children: ((state: NoInferTSelected) ComponentChild) | ComponentChild; }) ReturnTypeFunctionComponent;Subscribe允许你监听并响应表单状态的变化特别适合在执行副作用或按状态片段渲染组件时使用。它支持两个关键点selector从完整FormState中挑选所需的状态片段TSelected默认是整个FormState受NoInfer包裹以保证类型推断方向正确children既可以是普通ComponentChild也可以是一个接收TSelected并返回ComponentChild的函数渲染函数模式。四、源码剖析useForm如何构造扩展表单实例PreactFormExtendedApi并非只是“纸面上的类型”它由useFormHook 在运行时真实构造。核心实现位于 packages/preact-form/src/useForm.tsxconst extendedFormApi useMemo(() { const extendedApi: PreactFormExtendedApi... { ...formApi, handleSubmit: ((...props: never[]) { return formApi._handleSubmit(...props) }) as typeof formApi.handleSubmit, // We must add all getters from cores FormApi here, as otherwise the spread operator wont catch those get formId(): string { return formApi._formId }, get state() { return formApi.store.state }, } as never extendedApi.Field function APIField(props) { return Field {...props} form{formApi} / } extendedApi.FormGroup function APIFormGroup(props) { return FormGroup {...props} form{formApi} / } extendedApi.Subscribe function Subscribe(props: any) { return ( LocalSubscribe form{formApi} selector{props.selector} children{props.children} / ) } return extendedApi }, [formApi])这段实现有四个值得深挖的细节...formApi展开核心实例先展开核心FormApi的全部可枚举成员再手动补齐handleSubmit及formId、state两个 getter。源码注释明确说明必须把核心FormApi的所有 getter 在此处补全否则展开运算符无法捕获它们——这解释了交叉类型在运行时“合并”的完整做法。Field/FormGroup闭包绑定两个组件以函数组件形式包装并把核心的formApi实例通过formprop 传入Field {...props} form{formApi} /因此字段组件天然感知所属表单。Subscribe基于tanstack/preact-store的useSelectorLocalSubscribepackages/preact-form/src/useForm.tsx内部调用useSelector(form.store, selector)并配合functionalUpdate(children, data)将选中状态交给 children 渲染函数从而实现按需订阅、最小化重渲染。useMemo缓存 生命周期同步扩展实例仅在formApi变化时重建随后通过useIsomorphicLayoutEffect(formApi.mount, [])挂载表单、在每次渲染后调用formApi.update(opts)同步最新配置packages/preact-form/src/useForm.tsx。useForm的完整签名docs/framework/preact/reference/functions/useForm.md为function useFormTFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta(opts?): PreactFormExtendedApiTFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta;即useForm的返回值类型正是PreactFormExtendedApi——这就是该类型别名在整个 Preact 适配层中的枢纽地位。五、实战用法从 Quick Start 到仓库示例1. 一次性组件式写法useFormform.Fieldexamples/preact/simple/src/index.tsx 是仓库中最直接使用该类型别名的示例。useForm返回的form即PreactFormExtendedApi实例import { render } from preact import { useForm } from tanstack/preact-form import type { AnyFieldApi } from tanstack/preact-form function App() { const form useForm({ defaultValues: { firstName: , lastName: }, onSubmit: async ({ value }) { console.log(value) }, }) return ( form onSubmit{(e) { e.preventDefault() e.stopPropagation() void form.handleSubmit() }} form.Field namefirstName validators{{ onChange: ({ value }) !value ? A first name is required : value.length 3 ? First name must be at least 3 characters : undefined, }} children{(field) ( label htmlFor{field.name}First Name:/label input id{field.name} name{field.name} value{field.state.value} onBlur{field.handleBlur} onInput{(e) field.handleChange(e.currentTarget.value)} / FieldInfo field{field} / / )} / form.Subscribe selector{(state) [state.canSubmit, state.isSubmitting]} children{([canSubmit, isSubmitting]) ( button typesubmit disabled{!canSubmit} {isSubmitting ? ... : Submit} /button )} / /form ) }该示例同时用到了PreactFormExtendedApi的三个 Preact 专属成员form.Field声明字段name为类型安全的深度键拼写错误会在编译期报错form.Subscribe通过selector只订阅canSubmit与isSubmitting两个状态片段按需驱动按钮禁用态与提交文案核心层能力handleSubmit、reset则直接来自交叉类型左侧的FormApi。2. 官方 Quick Start 中的写法快速开始指南 展示了两种形式推荐方式——createFormHook预绑定组件减少样板代码import { render } from preact import { createFormHook, createFormHookContexts } from tanstack/preact-form import { TextField, NumberField, SubmitButton } from ~our-app/ui-library import { z } from zod const { fieldContext, formContext } createFormHookContexts() const { useAppForm } createFormHook({ fieldComponents: { TextField, NumberField }, formComponents: { SubmitButton }, fieldContext, formContext, }) const PeoplePage () { const form useAppForm({ defaultValues: { username: , age: 0 }, validators: { onInput: z.object({ username: z.string(), age: z.number().min(13), }), }, onSubmit: ({ value }) { alert(JSON.stringify(value, null, 2)) }, }) return ( form onSubmit{(e) { e.preventDefault() form.handleSubmit() }} form.AppField nameusername children{(field) field.TextField labelFull Name /} / form.AppField nameage children{(field) field.NumberField labelAge /} / form.AppForm form.SubmitButton / /form.AppForm /form ) }临时写法——直接使用useForm与form.Field即上节的示例风格。官方文档特别注明“All properties fromuseFormcan be used inuseAppFormand all properties fromform.Fieldcan be used inform.AppField。”也就是说useAppForm返回的AppFieldExtendedPreactFormApi也包含PreactFormExtendedApi的全部能力createFormHook 参考文档 中useAppForm的返回类型即由AppFieldExtendedPreactFormApi承载。3. 多步骤向导中的组合用法仓库的 multi-step-wizard 示例 展示了PreactFormExtendedApi在大型表单组合场景中的使用——通过createFormHook创建共享表单实例并在不同 step 子表单中复用form.Field、form.FormGroup等能力这体现了交叉类型在“核心状态管理 框架渲染能力”之外的组合价值。六、与其他参考文档的关系要深入理解PreactFormExtendedApi建议配套阅读以下仓库文档PreactFormApi 接口文档三大 Preact 专属成员的属性签名与TSelected泛型细节useForm 函数文档Hook 签名、opts参数类型及返回类型createFormHook 文档预绑定组件方案与useAppForm的关系FieldComponent 类型文档Field成员的类型形态Preact 框架指南目录docs/framework/preact/guides/校验、数组、字段组、表单组合等主题的完整实战指南。七、小结PreactFormExtendedApi是 TanStack Form Preact 适配层的类型枢纽它通过交叉类型将框架无关的FormApi与 Preact 专属的PreactFormApi合并12 个泛型参数把表单数据、11 类校验/提交回调与提交元数据全部纳入类型系统运行时则由useForm在useMemo中真实构造这一扩展实例并以闭包方式将Field、FormGroup、Subscribe绑定到核心表单实例上。理解这个类型别名就掌握了 TanStack Form 在 Preact 中“核心逻辑 框架渲染”双轨架构的接入点也为阅读后续所有 Preact 表单代码打下了类型层面的基础。【免费下载链接】form Headless, performant, and type-safe form state management for TS/JS, React, Vue, Angular, Solid, and Lit.项目地址: https://gitcode.com/GitHub_Trending/form/form创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考