我有以下两个界面:
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
如何声明这种类型?
答案 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;
// }
希望其中之一对您有用;祝你好运!
更新:既然我知道这些类型对您有用,那么我可以解释任何有关它们如何工作的困惑。其中有mapped和conditional类型,因此您可能需要阅读这些类型以获取更多详细信息。
因此Combine<U>
是一个映射类型,其键为U extends any ? keyof U : never
。这是一个distributed conditional type,它与keyof U
一样(不是您所指出的"e"
),不是。由于U
中的U extends any
是裸类型参数,因此,如果U
是联合类型,则编译器会将联合分解成其组成部分,对每个联合进行条件类型处理,然后然后合并结果。因此,当U
为ResLogin | 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'])
。
希望现在有意义。再次祝你好运!