首页 > 解决方案 > 根据数组文字区分联合

问题描述

我想使用数组中的文字从现有联合计算联合。

是否可以?

例子


interface Intf1 {
    type: "one";
    data: {
        param: boolean;
    };
}

interface Intf2 {
    type: "two";
    data: string;
}

interface Intf3 {
    type: "three";
    data: {
        param: number;
    };
}

const registered = ["one", "two"] as const;

type CombinedIntf = Intf1 | Intf2 | Intf3;

type CustomIntersect = ? 

const getObj = () => {
    return {} as any; // impl 
};

const obj: CustomIntersect<CombinedIntf, typeof registered> = getObj();
// Intf1 | Intf2

switch(obj.type) {
    case "one":
        console.log(obj.data.param);
    break;

    case "two":
        console.log(obj.param);
    break;

    case "three": // should give an error ""three" is not comparable to type "one", "two""
        console.log(obj.param);
    break;
}

谢谢!

标签: typescript

解决方案


我认为你可以通过这种方式实现你想要的:

type CustomIntersect<T, R extends readonly PropertyKey[]> =
  Extract<T, { type: R[number] }>

在这里,我使用了内置的Extract<T, U>实用程序类型,它返回T可分配给的联合的所有元素U。由于您希望第二个类型参数R是键数组的类型,因此我们将通过查找它的number-keyed 属性来获得其元素类型的并集,如R[number]. 通过ExtractingT可以分配给的所有工会成员{type: R[number]},我们应该只抓住那些你关心的成员:

const obj: CustomIntersect<CombinedIntf, typeof registered> = getObj();
// const obj: Intf1 | Intf2

switch (obj.type) {
  case "one":
    console.log(obj.data.param);
    break;

  case "two":
    console.log(obj.data.toUpperCase()); // <-- changed this
    break;

  case "three": // error!
    // ~~~~~~~
    // Type '"three"' is not comparable to type '"one" | "two"'.(2678)
    break;
}

Playground 代码链接


推荐阅读