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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 01:24:56