У меня есть следующие три интерфейса:
type A = {
x: string
y: string
}
type B = {
y: string
}
type C = {
z: string;
y: string;
}
Здесь есть общие обязательные свойства. И есть обязательные свойства, которые не являются частью остальных двух типов. Итак, мой вопрос:
Есть ли способ создать тип, который объединяет общие реквизиты и делает другие свойства необязательными? Например.:
type D = {
y: string
x?: string
z?: string
}
Любые идеи?






Работая над этим решением, я попытался создать реализацию, в которой использовались бы дженерики, но ничего не вышло. Я уверен, что мастер 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]
}
Есть довольно много способов сделать это, но одним из решений было бы создать тип 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;}
Я хотел поделиться более общим ответом на случай, если кто-то найдет его полезным.
Довольно обширно, но в конце концов вам нужно будет использовать только тип 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]>;
Во многих случаях тип объединения
A | B | Cбудет вести себя таким образом, но более строго в том смысле, что вы не можете иметь «необязательные» свойства более чем из одного из них.