如何约束函数参数(对象)遵循特定键名规则?
实现TypeScript中对对象键名和值类型的双重约束
要同时约束selectors对象的键名格式(必须为select${Capitalize<string>})和值类型(必须是Selector),可以通过TypeScript的模板字面量类型、映射类型和泛型约束来实现,具体步骤如下:
1. 定义基础类型
首先明确Selector的类型定义:
// 定义Selector类型,接收可选的状态泛型S,返回unknown type Selector<S = unknown> = (state: S) => unknown;
2. 创建键名验证工具类型
用模板字面量类型匹配符合要求的键名格式,排除仅select本身的情况:
// 验证键名是否符合 select${首字母大写字符串} 格式 type IsValidSelectorKey<K extends string> = K extends `select${Capitalize<infer Rest>}` ? Rest extends '' ? false : true // 排除仅"select"的键 : false;
3. 构建约束型的Selectors类型
通过映射类型,对每个键进行校验:不符合规则的键,其值类型会被设为never(或自定义错误类型),触发TypeScript报错:
// 约束selectors的键和值:键必须符合格式,值必须是Selector type ValidSelectors<T> = { [K in keyof T]: IsValidSelectorKey<string & K> extends true ? Selector // 键合法时,值必须是Selector类型 : never; // 键不合法时,值类型设为never,强制报错 };
4. 定义带泛型约束的函数
利用泛型让TypeScript自动推断传入的selectors类型,同时通过T extends ValidSelectors<T>强制约束:
function Foo<T extends ValidSelectors<T>>(arg: { name: string; selectors: T; }) { // 函数业务逻辑 }
测试效果
当传入不符合规则的参数时,TypeScript会精准报错:
Foo({ name: "Bar", selectors: { selectBuzz: () => {}, // ✅ 符合要求,无报错 fizz: () => {}, // ❌ 键名不符合格式,触发报错 selectFizz: '123', // ❌ 值类型不是Selector,触发报错 } });
优化错误提示(可选)
如果需要更友好的报错信息,可以把never替换为自定义错误类型:
type InvalidKeyMsg = "错误:键名必须遵循 `select${首字母大写字符串}` 格式(如selectBuzz)"; type InvalidValueMsg = "错误:值必须是Selector类型,即 (state: S) => unknown"; type ValidSelectors<T> = { [K in keyof T]: IsValidSelectorKey<string & K> extends true ? T[K] extends Selector ? T[K] : InvalidValueMsg : InvalidKeyMsg; };
此时报错会直接显示自定义提示,更易理解。
内容的提问来源于stack exchange,提问作者Dellirium
相关产品推荐
相关产品推荐

