Typescript泛型如何约束多props同键集且保证遍历类型安全
方案1:保留原API结构,解决Record遍历类型丢失问题
你原来的实现已经满足了多props同键集约束的核心需求,只需要两个优化点就能解决类型丢失问题:
第一步:精简泛型定义
你定义的泛型参数T没有实际使用,可以直接去掉,仅保留键的联合类型泛型K即可:
// 基础类型定义(根据你的实际业务调整) type FieldItem = { type: string; label: string; required: boolean }; type FieldOutput = { name: string; value: string | undefined }; type FormProps<K extends string> = { fields: Record<K, FieldItem>; initialValues: Record<K, string | undefined>; onSubmit: (value: Record<K, string | undefined>) => void; };
第二步:实现类型安全的遍历工具
原生Object.entries返回值默认是[string, unknown]类型,会丢失Record的键类型,你可以封装一个通用的typedEntries工具替代:
const typedEntries = <T extends Record<string, unknown>>(obj: T) => { return Object.entries(obj) as Array<[keyof T, T[keyof T]]>; };
优化后的组件代码
不需要你之前实现的iterator函数,也不需要手动类型断言:
export function FormBuilder<K extends string>(props: FormProps<K>) { const [fields, setFields] = useState<Record<K, string | undefined>>(props.initialValues); function handleSubmit(event: React.FormEvent<HTMLFormElement>) { event.preventDefault(); props.onSubmit(fields); } function handleChange(value: FieldOutput) { setFields(prev => ({ ...prev, [value.name as K]: value.value })); } return ( <form onSubmit={handleSubmit}> {typedEntries(fields).map(([key, value]) => ( <React.Fragment key={key}> <FieldFactory valueKey={key} value={value} field={props.fields[key]} onChange={handleChange} /> </React.Fragment> ))} </form> ); }
typedEntries可以全局复用,所有需要遍历Record的场景都可以使用,外层遍历onSubmit入参时也可以用这个工具保留类型
方案2:改用数组结构定义fields,天然支持遍历
如果不想处理Record遍历的问题,可以调整fields的结构为数组,同样能实现同键集约束:
类型定义
type FieldConfig<K extends string> = FieldItem & { name: K }; type FormProps<K extends string> = { fields: Array<FieldConfig<K>>; initialValues: Record<K, string | undefined>; onSubmit: (value: Record<K, string | undefined>) => void; };
使用示例
<FormBuilder initialValues={{ firstName: undefined, lastName: undefined, }} fields={[ { name: 'firstName', type: 'text', label: 'First Name', required: true }, { name: 'lastName', type: 'text', label: 'Last Name', required: true }, ]} onSubmit={value => { // value类型仍然是严格的{firstName?: string, lastName?: string} }} />
这种方案下fields本身是数组,可以直接遍历,不需要额外工具函数,TS会自动校验fields中所有name字段和initialValues的键完全一致,不符合时会抛出类型错误。
内容的提问来源于stack exchange,提问作者eivindml
相关产品推荐
相关产品推荐

