[TypeScript理论篇]使用集合论方法解决TypeScript问题

157 阅读1分钟

昨日一群友发出问题如下

type ABC = {
    a: number
    b: number
    c: number
}
// 上述类型应当至少有一个字段必须

我们来分析问题,先看现有条件, 现有索引集I={a,b,c}I = \{ a, b, c \}

而我们要得到的类型应当是

type Result = {
    a: number
    b?: number
    c?: number
} | {
    a?: number
    b: number
    c?: number
} | {
    a?: number
    b?: number
    c: number
}

这实质上就是一个含有三个元素的新集合, 可以看到这三个元素与索引集 II 有着明显的映射关系, 分别将其记为Aa,Ab,AcA_a, A_b, A_c

那么我们就可以得到一个索引族A={ Ai  iI }\mathscr{A}=\{~A_i~|~i\in I~\}, 将索引族中的集合合并, 就是我们需要的答案

于是我们现在需要做的就是写出AiA_i的通项公式, 显然, 这公式中包含两个变量, 对象类型 TT 和索引类型 KK , 每一项又由两部分组成: 一个字段必选, 其他所有字段可选, 我们将其表示为 kkK\{k}K \backslash \{k\}, 那么情况就很明了了, 我们很轻易就写出了如下类型

type Item<T, k extends keyof T> = {
    [key in Exclude<keyof T, k>]?: T[key]; // K\{k}
} & {
    [key in k]: T[key]; // {k}
};

再下一步就是进行映射获取索引族并进行合并了

type IndexedResult<T> = {
    [k in keyof T]: Item<T, k>; // 映射
};

type Result<T> = IndexedResult<T>[keyof T] // 因为值的类型本身就是Union, 此处直接就合并了

完整代码如下, 有兴趣的话自行测试下吧

type Item<T, k extends keyof T> = {
    [key in Exclude<keyof T, k>]?: T[key]; // K\{k}
} & {
    [key in k]: T[key]; // {k}
};

type IndexedResult<T> = {
    [k in keyof T]: Item<T, k>; // 映射
};

type Result<T> = IndexedResult<T>[keyof T] // 因为值的类型本身就是Union, 此处直接就合并了