同时获取交集和并集

时间:2019-05-03 01:31:47

标签: typescript

我有以下两个界面:

export interface ResLogin {
    e: "already logged in" | "fail";
    ticket: string;
}

export interface ResException {
    e: "exception";
    msg: string;
    stack: string;
}

我想要一个具有ResLogin和ResException的所有成员的类型,而'e'字段的可能值是“已经登录”,“失败”和“异常”。

这是我的尝试:

export type ResLoginClient = ResLogin & ResException;
export type ResLoginClient2 = ResLogin | ResException;

但是两者都有问题:

let r: ResLoginClient;
r.e // r.e has 'never' type
r.msg // r.msg exists and that's good

let r2: ResLoginClient2;
r2.e // type of r2.e is "already logged in" | "fail" | "exception"
r2.msg // but r2.msg does not exists

我想要一种声明类型的方式,以便

let r3:????
r3.e // type of r3.e is "already logged in" | "fail" | "exception"
r3.msg // exists
r3.ticket // exists

如何声明这种类型?

1 个答案:

答案 0 :(得分:0)

我不太了解您的用例,因此这种类型的功能可能不完全符合您的需求:

type Combine<U> = {
  [K in U extends any ? keyof U : never]: U extends { [P in K]?: any }
    ? U[K]
    : undefined
};

type ResLoginClient = Combine<ResLogin | ResException>;
// type ResLoginClient = {
//    e: "already logged in" | "fail" | "exception";
//    ticket: string | undefined;
//    msg: string | undefined;
//    stack: string | undefined;
// }

或者,如果您不想要这些undefined属性,则可以

type Combine<U> = {
  [K in U extends any ? keyof U : never]: U extends { [P in K]?: any }
    ? U[K]
    : never // undefined became never here
};

type ResLoginClient = Combine<ResLogin | ResException>;
// type ResLoginClient = {
//    e: "already logged in" | "fail" | "exception";
//    ticket: string;
//    msg: string;
//    stack: string;
// }

希望其中之一对您有用;祝你好运!


更新:既然我知道这些类型对您有用,那么我可以解释任何有关它们如何工作的困惑。其中有mappedconditional类型,因此您可能需要阅读这些类型以获取更多详细信息。

因此Combine<U>是一个映射类型,其键为U extends any ? keyof U : never。这是一个distributed conditional type,它与keyof U一样(不是您所指出的"e"),不是。由于U中的U extends any是裸类型参数,因此,如果U是联合类型,则编译器会将联合分解成其组成部分,对每个联合进行条件类型处理,然后然后合并结果。因此,当UResLogin | ResException时,条件类型U extends any ? keyof U : never的计算结果为

(ResLogin extends any ? keyof ResLogin : never) | 
(ResException extends any ? keyof ResException : never)

(keyof ResLogin) | (keyof ResException)

所以我要做的是通过keyof中的并集来对U函数进行 distribute 。因此,映射类型的K将遍历出现在U的至少一个组成部分中的键。

映射类型中的属性类似地分布。我们有U extends { [P in K]? any } ? U[K] : undefined。对于我们的U(ResLogin extends {[P in K]?: any} ? ResLogin[K] : undefined) | (ResException extends {[P in K]?: any} ? ResException[K] : undefined)。对于映射类型中的每个K都会对此进行评估,因此让我们来看一下具体性,例如"ticket"。其中{[P in K]?: any}仅是K的映射类型"ticket"被评估为具体类型{ticket?: any}。因此,"ticket"的{​​{1}}属性将被评估为:

Combine<U>

现在(ResLogin extends {ticket?: any} ? ResLogin['ticket'] : undefined) | (ResException extends {ticket?: any} ? ResException['ticket'] : undefined) 扩展了ResLogin,而{ticket?: any} 没有扩展了ResException,因此该属性是评估为:

{ticket?: any}

因此(ResLogin['ticket']) | (undefined) 贡献了其ResLogin属性,而"ticket"延续了ResException。如果我们查看出现在两个联合组成部分中的undefined而不是"ticket",则该属性将变为:

"e"

那里没有(ResLogin['e']) | (ResException['e'])

希望现在有意义。再次祝你好运!