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
相关产品推荐
相关产品推荐

