【问题标题】:Creating an Object type based on the parameters of multiple types under a union type根据联合类型下多个类型的参数创建Object类型
【发布时间】:2021-10-19 17:58:25
【问题描述】:

首先,我将提供一些背景信息:

我有两个接口,它们都继承了一个共同的基础。

interface Base { type: string }
interface Foo extends Base { type: "foo", name: string }
interface Bar extends Base { type: "bar", score: number }

我将两者都保存在联合类型中,因此我可以在存储两者之一的数组中使用它。

type Baz = Foo | Bar;
const storage : Baz[] = [];

我有一个存储联合类型所有可能的type 值的类型,以及一个只接受这些值的函数。

type BazTypes = Baz["type"]; // "foo" | "bar"
function readBaz(type : BazTypes) { /* ... */ }

现在,我正在尝试制作一个函数映射,它将解析Baz 提供的给定对象,给定它的类型,如下所示:

// Expectation:
{
    "foo": (arg : Foo) => boolean, // (arg: {type: "foo", name: string}) => boolean
    "bar": (arg : Bar) => boolean, // (arg: {type: "bar", score: number}) => boolean
}

type BazMap = {
    [key in BazTypes]: (args : Baz extends {type: key} ? Baz : never) => boolean
};
const parsers : BazMap = {
    "foo": (foo /* which should be of type Foo */) => { /* ... foo.name ... */ },
    "bar": (bar /* which should be of type Bar */) => { /* ... bar.score ... */ },
}

但是,该映射反而提供联合类型本身,而不是给定类型的正确接口。

// Reality:
{
    "foo": (arg : Foo | Bar) => boolean,
    "bar": (arg : Foo | Bar) => boolean
}

type BazMap = {
    [key in BazTypes]: (args : Baz extends {type: key} ? Baz : never) => boolean
};
const parsers : BazMap = {
    "foo": (foo /* which is Foo | Bar */) => { /* Only foo.type can be accessed. */ },
    "bar": (bar /* which is Foo | Bar */) => { /* Only bar.type can be accessed. */ },
}

因此,TypeScript 不提供namescore,因为FooBar 中都不存在这两个值。

我正在尝试按预期获取地图。到目前为止,我还不能正确地创建一个接受正确类型的地图。

TL;DR:如何根据类型的参数创建 Object 类型?(例如,如果类型是 "foo",它将返回 Foo 的解析器(即不接受Bar)等)

【问题讨论】:

    标签: typescript types


    【解决方案1】:

    您可以使用来自this answerUnionToIntersection

    type UnionToIntersection<U> =
      (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
    
    type Parsers<T extends Base> = UnionToIntersection<
      // Distributes T
      T extends any
        // {foo: (args: Foo) => boolean} but with each T
        ? {[_ in T['type']]: (args: T) => boolean}
        : never
    >;
    
    // {foo: (args: Foo) => boolean} & {bar: (args: Bar) => boolean}
    type BazMap = Parsers<Baz>;
    

    Playground link

    【讨论】:

      【解决方案2】:

      @cherryblossom 的回答非常巧妙,但这里有一个更简单的方法:

      type BazMap = {
          [key in BazTypes]: (args: Baz & {type: key}) => boolean
      };
      

      我只是在您的设置中更改导致错误的一行。

      您的版本不起作用,因为您的条件 Baz extends {type: key} ? Baz : never 将永远不会返回。 Baz 不是通用的。您是在询问您预定义的联合类型 Baz,并且作为联合类型,它不会扩展其成员的 {type: __} 属性。

      【讨论】:

        猜你喜欢
        • 2021-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-08
        • 2020-08-04
        • 1970-01-01
        相关资源
        最近更新 更多