方法重载:捕获已知参数并回退未知参数

问题描述 投票:0回答:1

我有一个遗留项目,它有一个基类,它表示在运行时构建的复杂对象。这些对象具有状态,并具有状态属性的 getter/setter 方法。

在某些情况下,状态属性是众所周知的,在这种情况下我想强制执行严格的类型检查。但是,当使用未知的状态属性时,我需要后备。

以下是实践中的基本概念:

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();

// Overloads match as desired:
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 在尝试匹配重载方面非常慷慨(因为它应该如此),因此如果我传入具有无效类型的已知属性,我不会收到任何错误报告:

// Should throw errors
foo.set("name", 37); // expected a string
foo.set("age", "Steve"); // expected a number

我认为使用条件类型可能会“捕获”已知属性并引发错误:

{
    set<P extends keyof KnownProps, V>(key: P, value:V extends KnownProps[P] ? KnownProps[P] : never): void;
}

但是,唉,TypeScript 仍然通过“后备”重载慷慨地接受不正确的类型定义。

typescript generics overloading
1个回答
0
投票

在“键”上使用条件类型,而不是值。从 TypeScript 5.0 开始,请确保在类型参数上使用

const
修饰符,以便它与文字属性名称匹配,而不是将类型扩展为
string
:

{
    set<const P>(key: P, value:P extends keyof KnownProps ? KnownProps[P] : unknown): void;
    set(key:string, value:unknown):void {
        this.state[key] = value;
    }
}

您现在应该会收到预期的错误:

// Overloads match as desired:
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;

// Known properties now apply strict type checking:
foo.set("name", 37);
//              ~~
//              Argument of type 'number' is not assignable to parameter of type 'string'.
foo.set("age", "Steve");
//             ~~~~~~~
//             Argument of type 'string' is not assignable to parameter of type 'number'.

在 TypeScript 5.0 之前,您可以遵循上面的建议,但是您必须通过调用

as const
来使用
set()
才能获得相同的行为:

/////////////////////////
// BEFORE TYPESCRIPT 5.0:

{
    set<P>(key: P, value:P extends keyof KnownProps ? KnownProps[P] : unknown): void;
    set(key:string, value:unknown):void {
        this.state[key] = value;
    }
}

foo.set("name" as const, 37);
//                       ~~
//                       Argument of type 'number' is not assignable to parameter of type 'string'.
foo.set("age" as const, "Steve");
//                      ~~~~~~~
//                      Argument of type 'string' is not assignable to parameter of type 'number'.

© www.soinside.com 2019 - 2024. All rights reserved.