从标记的联合类型接口返回匹配类型

时间:2018-02-07 00:52:40

标签: typescript

众所周知,您可以在查询后使用标记的联合类型来获得正确的类型:

export interface A {
    type: 'a'
    foo: string
}

export interface B {
    type: 'b'
    bar: string
}

export type U = A | B

const a: U = {}

switch (a.type) {
    case 'a':
        // is now A
        console.log(a.foo) // .foo is a string
        break
}

但是,在动态查找时,我无法找到动态获取正确类型的解决方案:

export interface A {
    type: 'a'
    foo: string
}

export interface B {
    type: 'b'
    bar: string
}

export type U = A | B

class Foo {
    private items: U[]

    public get(name: U['type']) {
        return this.items.find((i) => i.type === name)
    }
}

const foo = new Foo()

const a = foo.get('a')
a // a is U but should be A

似乎打字稿不能自动找出类型。

我也尝试过使用泛型类型,我希望打字稿可以猜到,就像那样:

public get<T extends U>(name: T['type']): T {
    return this.items.find((i) => i.type === name)
}

我希望T是由T ['type']选择的界面,但它有相同的结果。

1 个答案:

答案 0 :(得分:1)

不幸的是,打字稿不会像你描述的那样进行“向后”推理(例如,我们不能自动从某种类型的字段转到那种类型)。相反,一种解决方案是明确描述您的关系。然后很容易得到你正在寻找的行为:

type UMap = {
    "a": A;
    "b": B;
}

class Foo {
    private items: U[]

    public get<K extends keyof UMap>(name: K): UMap[K] {
        return this.items.find((i) => i.type === name)
    }
}

const a = foo.get('a') // a has type A
const b = foo.get('b') // b has type B

我还建议从地图生成类型U,而不是明确声明:

type U = UMap[keyof UMap]; // A | B

这样,您可以在向联合添加更多类型时,只需要在一个位置更新内容。