Svelte 5表单库:如何从子组件推断FormData类型?
实现Svelte 5表单库的类型安全与自动推断
完全可行,借助Svelte 5的泛型组件和插槽类型推断能力,你可以从子组件Input的key和type属性自动推导表单提交数据的具体类型,实现完整的TypeScript类型安全和VSCode自动补全。
核心实现步骤
1. 定义Input组件的类型映射
先给Input组件建立type属性到TS类型的映射关系,让不同的输入类型对应明确的TS类型:
<!-- Input.svelte --> <script lang="ts"> // 映射input类型到对应的TS类型 type InputTypeMap = { text: string; email: string; password: string; number: number; checkbox: boolean; date: Date; // 可根据需求扩展更多类型 }; // Input组件的Props类型,绑定key和type的关联关系 export type InputProps<K extends string, T extends keyof InputTypeMap> = { key: K; label: string; icon?: string; type: T; // 可添加其他props如required、placeholder等 }; // 声明组件props,使用泛型约束 export let key: K; export let label: string; export let icon: string | undefined = undefined; export let type: T = "text" as T; </script> <!-- 组件DOM结构示例 --> <div class="input-field"> {#if icon} <span class="icon">{icon}</span> {/if} <label for={key}>{label}</label> <input id={key} type={type} data-form-key={key} /> </div>
2. 实现Form组件的类型推断逻辑
在Form组件中,利用Svelte 5的$$Slots获取插槽内Input组件的props类型,自动推导表单数据的结构:
<!-- Form.svelte --> <script lang="ts"> import type { InputProps, InputTypeMap } from "./Input.svelte"; // 从Input组件的Props集合中提取表单数据类型 type ExtractFormData<Inputs extends InputProps<any, any>[]> = Inputs[number] extends InputProps<infer K, infer T> ? { [Key in K]: InputTypeMap[T] } : never; // Form组件的泛型Props,默认从插槽推断类型 export type FormProps<Data extends Record<string, any> = ExtractFormData<typeof $$Slots.default[0]>> = { onapprove: (data: Data) => Promise<void> | void; }; export let onapprove: FormProps<Data>["onapprove"]; // 你的后台Form类,添加泛型支持 class FormInstance<Data> { private formData: Partial<Data> = {}; // 模拟收集表单数据的逻辑(实际可通过DOM查询或双向绑定实现) private collectData(): Data { // 示例:遍历所有带data-form-key的input元素 const inputs = document.querySelectorAll("[data-form-key]"); inputs.forEach(input => { const key = input.getAttribute("data-form-key") as keyof Data; const type = input.type as keyof InputTypeMap; // 根据input类型转换值 switch (type) { case "number": this.formData[key] = Number(input.value) as Data[keyof Data]; break; case "checkbox": this.formData[key] = (input as HTMLInputElement).checked as Data[keyof Data]; break; case "date": this.formData[key] = new Date(input.value) as Data[keyof Data]; break; default: this.formData[key] = input.value as Data[keyof Data]; } }); return this.formData as Data; } // 提交方法,验证通过后触发onapprove async submit() { // 模拟验证逻辑 const isValid = true; if (isValid) { const data = this.collectData(); await onapprove(data); } } } // 导出form实例,绑定给父组件 export let form: FormInstance<Data> = new FormInstance<Data>(); </script> <form onsubmit|preventDefault={() => form.submit()}> <slot /> </form>
3. 使用示例(自动推断类型)
在使用时,无需手动指定类型,TS会自动从Input的key和type推导handleApprove中data的类型:
<!-- LoginForm.svelte --> <script lang="ts"> import Form from "./Form.svelte"; import Input from "./Input.svelte"; const handleApprove = async (data) => { // 这里data会被自动推断为:{ email: string; password: string } console.log(data.email); // VSCode会自动补全 console.log(data.password); // 如果访问不存在的键(如data.username),TS会直接报错 }; </script> <Form bind:form onapprove={handleApprove}> <Input key="email" label="邮箱" icon="mail" type="email" /> <Input key="password" label="密码" icon="key" type="password" /> <button onclick={() => form.submit()}>登录</button> </Form>
4. 动态表单场景的显式类型指定
如果表单是动态生成的(比如循环渲染Input),无法通过插槽自动推断时,可以显式给Form组件指定泛型:
<script lang="ts"> import Form from "./Form.svelte"; import Input from "./Input.svelte"; // 显式定义表单数据类型 type UserFormData = { username: string; age: number; agree: boolean; }; const handleApprove = async (data: UserFormData) => { // 类型完全匹配 }; </script> <Form<UserFormData> bind:form onapprove={handleApprove}> {#each Object.entries(formFields) as [key, field]} <Input {key} label={field.label} type={field.type} /> {/each} <button onclick={() => form.submit()}>提交</button> </Form>
内容的提问来源于stack exchange,提问作者Hovadko
相关产品推荐
相关产品推荐

