创建具有来自其他类型和其他可选的公共道具的类型



我有以下三个接口:

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>,该类型具有T1T3三个类型共享的属性,并将其与一个对象Partial<T1 & T2 & T3>相交,该对象具有T1T3中任何属性的可选属性。

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]>;

游乐场

最新更新