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

TypeScript中约束两个属性对象键名完全一致的实现方法

TypeScript 实现两个属性键名完全一致的约束

不需要预先定义固定键名集合,通过泛型+映射类型即可实现双向的键名一致性校验,完全满足分段表单的类型约束需求。

核心实现

原接口使用字符串索引签名时,TS 无法对两个属性的键集合做关联校验,我们需要把接口改造为带泛型的结构,同时通过类型映射禁止额外键、强制要求必填键:

import * as Yup from 'yup';
import type { AnySchema } from 'yup';

type Section<T extends Record<string, any>> = {
  initialValues: T;
  // 第一层映射:要求initialValues里的所有键,validationSchema必须有对应schema
  // 第二层交叉约束:禁止validationSchema出现initialValues里不存在的键,不存在的键值类型为never
  validationSchema: { [K in keyof T]: AnySchema } & {
    [K in string]: K extends keyof T ? AnySchema : never;
  };
};

// 辅助创建函数:自动推断initialValues的键类型,无需手动传入泛型参数
const defineSection = <T extends Record<string, any>>(config: Section<T>): Section<T> => config;

使用效果

合法示例(无类型错误)

const section1 = defineSection({
  initialValues: {
    name: "",
    age: undefined // 对应数字类型值
  },
  validationSchema: {
    name: Yup.string().required(),
    age: Yup.number().required()
  }
});

非法示例(TS 抛出类型错误)

const badSection = defineSection({
  initialValues: {
    name: "",
    age: undefined
  },
  validationSchema: {
    name: Yup.string().required(),
    height: Yup.number().required(), // TS报错:Yup.NumberSchema不能赋值给类型never,因为height不存在于initialValues中
    // TS同时会报错:缺少属性"age"
  }
});

补充说明

  • 约束是双向生效的:既不允许校验Schema少配初始值中存在的字段,也不允许校验Schema多出初始值中不存在的字段,保证两个属性的键名完全一致
  • 不需要提前维护固定的字段名联合类型,TS会根据传入的initialValues自动推断键集合,适配任意字段的分段表单场景
  • 如果不想用辅助函数,也可以手动传入泛型参数声明类型,例如const section: Section<{name: string; age?: number}> = { ... },但辅助函数的写法更简洁,不会出现泛型参数和实际值不匹配的问题

内容的提问来源于stack exchange,提问作者Seth Lutske

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 02:27:29