TypeScript方法重载问题:已知属性传错类型无法触发类型校验
实现已知属性严格类型校验+未知属性兼容的TypeScript Setter/Getter
维护遗留项目时,需要实现类的set/get方法:对已知状态属性执行严格类型校验,对未知属性保留兼容处理。但常规重载方案无法在已知属性传入错误类型时触发报错。
原实现代码
interface KnownProps { name: string; age: number; } class Foo { private state: Record<string, unknown> = {}; set<P extends keyof KnownProps>(key: P, value: KnownProps[P]): void; set(key: string, value: unknown): void; set(key: string, value: unknown): void { this.state[key] = value; } get<P extends keyof KnownProps>(key: P): KnownProps[P]; get(key: string): unknown; get(key: string): unknown { return this.state[key]; } } const foo = new Foo(); // 符合预期的正常调用 foo.set("name", "Steve"); // key: "name", value: string foo.set("age", 37); // key: "age", value: number foo.set("birthday", new Date()); // key: "birthday", value: unknown const name = foo.get("name"); // string const age = foo.get("age"); // number const birthday = foo.get("birthday"); // unknown;
存在的问题
TypeScript会优先匹配最宽泛的重载(set(key:string, value:unknown)),导致传入已知属性但类型错误时,无法触发预期的类型报错:
// 理应报错但未触发的错误调用 foo.set("name", 37); // 期望传入string,实际传number foo.set("age", "Steve"); // 期望传入number,实际传string
解决方案
核心思路是缩小第二个重载的匹配范围,让它仅处理KnownProps之外的未知key,而非所有string类型。通过Exclude工具类型定义未知属性的key类型,限制重载的匹配边界:
interface KnownProps { name: string; age: number; } // 定义未知属性key:排除KnownProps的所有key type UnknownPropKey = Exclude<string, keyof KnownProps>; class Foo { private state: Record<string, unknown> = {}; // 第一个重载:处理已知属性,严格校验类型 set<P extends keyof KnownProps>(key: P, value: KnownProps[P]): void; // 第二个重载:仅处理未知属性,key必须属于UnknownPropKey set(key: UnknownPropKey, value: unknown): void; set(key: string, value: unknown): void { this.state[key] = value; } get<P extends keyof KnownProps>(key: P): KnownProps[P]; get(key: UnknownPropKey): unknown; get(key: string): unknown { return this.state[key]; } } const foo = new Foo(); // 正常调用依然生效 foo.set("name", "Steve"); // ✅ foo.set("age", 37); // ✅ foo.set("birthday", new Date()); // ✅ const name = foo.get("name"); // string const age = foo.get("age"); // number const birthday = foo.get("birthday"); // unknown; // 错误调用现在会正确触发类型报错 foo.set("name", 37); // ❌ 类型“number”的参数不能赋给类型“string”的参数 foo.set("age", "Steve"); // ❌ 类型“string”的参数不能赋给类型“number”的参数
原理说明
Exclude<string, keyof KnownProps>会生成所有不属于KnownProps的string类型key,确保第二个重载仅匹配未知属性。- 当传入已知key但类型错误时,既匹配不上第一个重载(类型不兼容),也匹配不上第二个重载(key不在
UnknownPropKey范围内),从而触发TypeScript的类型错误提示。
内容的提问来源于stack exchange,提问作者JDB
相关产品推荐
相关产品推荐

