You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 12:36:04