【问题标题】:Mapped object type with generic per key in TypeScriptTypeScript 中每个键的泛型映射对象类型
【发布时间】:2021-03-28 07:36:17
【问题描述】:

假设我有一个类型,它是由两项组成的任意数组:参数列表和参数与参数匹配的函数。为了方便起见,我创建了一个可以推断泛型类型的函数。

type MapParamsToFunction<A extends any[]> = [[...params: A], (...args: A) => void]

function asMap<A extends any[]>(map: MapParamsToFunction<A>): MapParamsToFunction<A> {
    return map;
}

asMap([[],() => {}]) // good
asMap([[0, "Hello World", 2], (a: number, b: string, c: number) => { }]); // good
asMap([[0, "Hello World", 2], (a: number, b: number, c: number) => { }]); // error
asMap([[0, "Hello World"], (a: number, b: string, c: number) => { }]); // error

目前没有问题。现在,我想使用它并创建一个字典,其中每个键可以有不同的参数/参数列表。但是,我看不到让 TypeScript 在每个键上使用不同泛型的方法。

我尝试在类型上使用any[],但如果参数与参数不匹配,它不会引发类型错误。

type FunctionDictionary<T extends string> = {
    [K in T]: MapParamsToFunction<any[]>
}

function asDictionary<T extends string>(dict: FunctionDictionary<T>): FunctionDictionary<T> {
    return dict;
}

let obj = asDictionary({
    "foo": [[0, "Hello World", 2], (a: number, b: number, c: number) => { }], // no type error
    "bar": [["","",""], (a: string, b: string, c: string) => { }]
});

有没有办法映射这个,所以每个参数都可以有它自己的通用参数列表?

type FunctionDictionary<T extends string> = {
    [K in T]: MapParamsToFunction<?> // <--- what goes here
}

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    TypeScript 没有 existential types 来表示“a MapParamsToFunction&lt;X&gt; for some type X”,因此您不能只将 FunctionDictionary 记录下来。幸运的是,它确实允许 inference from mapped types,因此您可以采用 对象类型 T,其属性都是参数列表,并将每个属性 K 映射到适当的 MapParamsToFunction&lt;T[K]&gt;

    type FunctionDictionary<T extends Record<keyof T, any[]>> = {
        [K in keyof T]: MapParamsToFunction<T[K]>
    }
    

    然后您的asDictionary() 将从传入的FunctionDictionary&lt;T&gt; 中推断出T 对象,如下所示:

    function asDictionary<T extends Record<keyof T, any[]>>(
        dict: FunctionDictionary<T>
    ): FunctionDictionary<T> {
        return dict;
    }
    

    你得到你想要的行为:

    let obj = asDictionary({
        "foo": [[0, "Hello World", 2], (a: number, b: number, c: number) => { }], // error!
        // --------------------------> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // Types of parameters 'b' and 'args_1' are incompatible.
        "bar": [["", "", ""], (a: string, b: string, c: string) => { }]
    });
    

    Playground link to code

    【讨论】:

    • 您在此处的回答帮助我弄清楚了如何解决一个不同但相关的问题,该问题围绕参数化 api 对象,其中键是方法名称,值是提取器函数,如 (p: Params) =&gt; Promise&lt;Response&gt;。显示T 可以在T extends Record&lt;keyof T, any[]&gt; 中引用自身的示例特别有用。谢谢! twitter.com/davidcrespo/status/1380555171130920961
    猜你喜欢
    • 2018-06-25
    • 2018-04-04
    • 2021-04-02
    • 2017-08-08
    • 2019-12-21
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    相关资源
    最近更新 更多