Typescript获取联合类型的有效组合

wqlqzqxt  于 2023-02-17  发布在  TypeScript
关注(0)|答案(1)|浏览(114)

假设我有如下联合类型:

NumberOfChildren = "0" | "1" | "2" | "3+"
EldestAge = "-1" | "0-3" | "3-10" | "11-17" | "18+" // -1 for null
NextEldestAge = "-1" | "0-3" | "3-10" | "11-17" | "18+" // -1 for null

如果我做${NumberOfChildren}_${EldestAge}_${NextEldestAge}
我将得到无效的组合,如:

0_0-3_-1 // Can't have an Eldest age with no children
2_3-10_18+ // Next Eldest can't be older than eldest

我试过使用条件类型,例如:

type combos<count extends string, age1 extends string, age2 extends string> =
| ([count, age1, age2] extends ["0", "-1", "-1"] ? "0_-1_-1" : "0_-1_-1")
| ([1, age1, -1] extends ["1", age1, "-1"] ? "1_${age1}_-1" : "0_-1_-1")
.... more combos

但唯一出来的是“0_-1_-1”。
正确的方法是什么?最终目标是一个联合类型,其中所有类型的有效组合都用_连接
编辑:根据vera的评论,这是一个解决方案:

type NumberOfChildren = "0" | "1" | "2" | "3+"
type AgeGroups = ["-1", "0-3", "3-10", "11-17", "18+"] // -1 for null

type Builder<Age extends string, AgeGroup extends string[], Previous extends string[] = []> =
    AgeGroup extends [infer Head extends string, ...infer Tail extends string[]]
        ? `${Age}_${Head}_${Exclude<Head | Previous[number], "-1">}` | Builder<Age, Tail, [...Previous, Head]>
        : never;

type ZeroChildren = `0_-1_-1`;
type OneChild = `1_${AgeGroups[number]}_-1`;
type TwoChildren = Builder<"2", AgeGroups>
type ThreeOrMoreChildren = Builder<"3", AgeGroups>;

type Test = TwoChildren;
//   ^?

type ValidCombos = ZeroChildren | OneChild | TwoChildren | ThreeOrMoreChildren;
//   ^?
mspsb9vt

mspsb9vt1#

使用元组来描述我们的年龄组会使实现更容易一些:

type AgeGroups = ["-1", "0-3", "3-10", "11-17", "18+"];

让我们分别处理所有不同的情况:0个子项、1个子项、2个子项和3个或更多。虽然您可以编写一些复杂类型来完成这一切,但我认为将它们分开覆盖更容易阅读和维护。
对于零个孩子,只有一种情况可以硬编码:

type ZeroChildren = `0_-1_-1`;

接下来是一个孩子,也很简单:

// exclude -1 as first child
type OneChild = `1_${Exclude<AgeGroups[number], "-1">}_-1`;

但对于两个孩子,我们需要更多的逻辑,因为第二个孩子必须比第一个孩子小(或同龄):

type TwoChildren<A extends string[] = AgeGroups, Previous extends string[] = []> =
    A extends [infer Head extends string, ...infer Tail extends string[]]
        ? `2_${Head}_${Exclude<Head | Previous[number], "-1">}` | TwoChildren<Tail, [...Previous, Head]>
        : never;

由于AgeGroups是一个元组,所以我们可以“迭代”它,每次迭代都存储所有之前已经迭代过的元素,然后在下一次迭代中,我们可以在结果中使用之前的元素(仍然要确保排除“-1”)。
三个或三个以上的孩子应与两个孩子一样处理:

type ThreeOrMoreChildren = TwoChildren;

所以当你完成的时候你会得到

type ValidCombos = ZeroChildren | OneChild | TwoChildren | ThreeOrMoreChildren;

其可以被简化为

type ValidCombos = ZeroChildren | OneChild | TwoChildren;

Playground

相关问题