我有以下三个接口:
type A = {
x: string
y: string
}
type B = {
y: string
}
type C = {
z: string;
y: string;
}
这里有常见的必需属性。还有一些必需的属性不属于其余两种类型。所以,我的问题是:
有没有办法创建一种类型,将普通道具联合起来,并使其他属性可选?例如:
type D = {
y: string
x?: string
z?: string
}
有什么想法吗?
有很多方法可以做到这一点,但一种解决方案是创建一个类型Pick<T1, keyof T1 & keyof T2 & keyof T3>
,该类型具有T1
到T3
三个类型共享的属性,并将其与一个对象Partial<T1 & T2 & T3>
相交,该对象具有T1
到T3
中任何属性的可选属性。
type CommonRequiredRestOptional<T1, T2, T3> =
Pick<T1, keyof T1 & keyof T2 & keyof T3> & Partial<T1 & T2 & T3>
部分对象也将包含所需的属性,但由于相交,这些属性仍然是必需的(T & (T | undefined)
是T
(。
助手TopLevelNormalize
可用于显示实际对象类型,而不是Pick<A, "y"> & Partial<A & B & C>
:
type TopLevelNormalize<T> = T extends infer S ? {[K in keyof S]: S[K]} : never;
type Test = TopLevelNormalize<CommonRequiredRestOptional<A, B, C>>
// type Test = { y: string; x?: string | undefined; z?: string | undefined;}
TypeScript游乐场
在开发这个解决方案时,我试图创建一个使用泛型的实现,但没有取得任何进展。我相信TypeScript向导可以从我的中派生出一个通用的解决方案
首先,让我们创建一个实用程序类型,用于查找";"公共":
type Common = {
[key in keyof A & keyof B & keyof C]: A[key] | B[key] | C[key]
}
现在我们可以从您的一个接口中找到";不常见":
{
// Properties only in A
[key in keyof Exclude<A, keyof Common>]?: A[key]
}
把这些放在一起,我们得到了:
export interface A {
x: string
y: string
}
export interface B {
y: string
}
export interface C {
z: string
y: string
}
/**
* Common properties in A, B, and C.
*/
type Common = {
[key in keyof A & keyof B & keyof C]: A[key] | B[key] | C[key]
}
/**
* All properties from A, B, and C.
*
* Properties that are "common" (in all interfaces) are required,
* others (not present in all interfaces) are optional.
*/
type D = Common & {
// Properties only in A
[key in keyof Exclude<A, keyof Common>]?: A[key]
} & {
// Properties only in B
[key in keyof Exclude<B, keyof Common>]?: B[key]
} & {
// Properties only in C
[key in keyof Exclude<C, keyof Common>]?: C[key]
}
TS游乐场
我想分享一个更通用的答案,以防有人觉得它有用。
相当广泛,但最终您只需要使用CommonUncommon
类型(因为没有更好的名称(
export interface A {
x: string;
y: string;
}
export interface B {
y: string;
}
export interface C {
z: string;
y: string;
}
export interface D {
y: string;
justAnotherProp: unknown[];
}
type IsInAll<T extends unknown[], Prop extends number | string | Symbol> = T extends [infer Head, ...infer Tail]
? Prop extends keyof Head
? IsInAll<Tail, Prop>
: false
: true;
type ExcludeArr<T extends unknown[], X, Res extends unknown[] = []> = T extends [infer Head, ...infer Tail]
? Head extends X
? X extends Head
? ExcludeArr<Tail, X, Res>
: ExcludeArr<Tail, X, [...Res, Head]>
: ExcludeArr<Tail, X, [...Res, Head]>
: Res;
type Normalize<T> = {[K in keyof T]: T[K]};
type Unite<T extends unknown[], K extends unknown[] = T, Res = unknown> = T extends [infer Head, ...infer Tail]
? Unite<Tail, K, {
[Prop in keyof Head as true extends IsInAll<ExcludeArr<K, Head>, Prop> ? Prop : never]: Head[Prop];
}
&
{
[Prop in keyof Head as false extends IsInAll<ExcludeArr<K, Head>, Prop> ? Prop : never]?: Head[Prop];
}
&
Res>
: Res;
type CommonUncommon<T extends unknown[]> = Normalize<Unite<T>>;
type res1 = CommonUncommon<[A, B]>;
type res2 = CommonUncommon<[A, B, C]>;
type res3 = CommonUncommon<[A, B, C, D]>;
游乐场