TypeScript类方法如何接收interface的keyof并校验参数?
TypeScript Check类test方法的类型校验实现方案
定义的类型与对象
interface Types { string: (value: string, letterAllowed: string[]) => boolean; number: (value: number, maxNumber: number) => boolean; } const types: Types = { string: (value: string) => typeof value === "string", number: (value: number) => typeof value === "number", };
核心需求
- 调用
test方法时,必须传入Types的keyof类型参数,否则触发类型报错; - 传入的后续参数必须严格匹配对应key的函数除第一个参数外的类型,参数缺失或类型不匹配时触发报错。
期望效果示例
// 报错:参数""不属于"string" | "number"类型 new Check("value").test(""); // 报错:缺少"number"类型对应的maxNumber参数 new Check(5).test("number"); // 无报错:参数完全符合要求 new Check(5).test("number", 10);
之前尝试的代码及报错
首次尝试代码
class Typeof { value: any; constructor(value: any) { this.value = value; } is<T extends keyof Types>(type: T, ...args: OmitFirstArgument<Types[T]>): boolean { return types[type](this.value, ...args); } }
报错信息:TS2556: 展开参数必须是元组类型或传递给剩余参数。
修改后尝试代码
class TypeOf<T> { value: T; constructor(value: T) { this.value = value; } is<A extends (keyof Types | string)>(name: A, ...args: OmitFirstArgument<Types[A]>) { return types[name as any].apply(this, [this.value, ...args]); } }
报错信息:TS2536: 类型"A"不能用于索引类型"Types"。
最终解决方案
步骤1:定义OmitFirstArgument类型工具
明确提取函数除第一个参数外的剩余参数类型:
type OmitFirstArgument<T extends (...args: any[]) => any> = T extends (_: any, ...rest: infer R) => any ? R : never;
步骤2:实现符合需求的Check类
class Check<T> { private value: T; constructor(value: T) { this.value = value; } test<K extends keyof Types>(type: K, ...args: OmitFirstArgument<Types[K]>): boolean { // 通过类型断言确保value匹配对应校验函数的第一个参数类型 return types[type](this.value as Parameters<Types[K]>[0], ...args); } }
报错原因解析
- TS2556报错:原代码中TypeScript无法明确
args的元组类型与types[type]的剩余参数类型完全匹配,通过OmitFirstArgument明确提取剩余参数为元组类型,再配合类型断言确保value的类型兼容性,即可解决展开参数的类型校验问题。 - TS2536报错:原代码将泛型
A扩展为keyof Types | string,当A为普通string类型时,无法索引Types接口。直接限制泛型K为keyof Types,既能满足“必须传入合法key”的需求,又避免了非法索引的问题。
内容的提问来源于stack exchange,提问作者Meslzy
相关产品推荐
相关产品推荐

