typescript - Combinarea DeepPartial/DeepMerge/Limitarea genericului


Încerc să creez un generic destul de complicat. Am pus multe întrebări pe StackOverflow pentru a încerca să asamblez ceea ce vreau, așa că m-am gândit să explic pur și simplu ce caut.

Având o interfață:

type Person = {
  name: string,
  age: number,
  address: {
    line1: string,
    line2: string | null | number,
    zip: string | number,
    address: {
      line1: string,
      line2: string | null | number,
      zip: string | number,
    } | string,
  },
};

Caut o modalitate de a putea crea o altă interfață pornind de la această interfață, care să limiteze o parte din domeniul său de aplicare, păstrând în același timp o parte din acesta.

Exemple

De exemplu, mi-ar plăcea să am capacitatea de a

  • limitează Person.address.address la doar un șir
type PersonEdit = {
  address: {
    address: string,
  },
};
  • limitează Person.address.address doar la un obiect
type PersonEdit = {
  address: {
    address: {},
  },
};
  • schimbați orice proprietate în opțional,
type PersonEdit = {
  address: {
    address?: {} | string,
  },
};
  • limitează Person.address.address.zip la doar un număr
type PersonEdit = {
  address: {
    address: {
      zip: number
    },
  },
};

Cerințe

  • Mi-ar plăcea să pot să mă concentrez doar pe proprietatea imbricată și să moștenesc toate celelalte tipuri din restul interfeței.
  • Aceste Edits ar trebui să fie un DeepPartial<> al sursei și să nu permită proprietăți noi sau uniuni care nu se află în părinte.

Generice existente

Iată ce au reușit să mă ajute să găsesc cei minunați de pe StackOverflow până acum din alte întrebări pe care le-am pus.

type Confine<A, B extends { [K in keyof B]: K extends keyof A ? A[K] : never }> = B;
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends (infer U)[]
  ? DeepPartial<U>[]
  : T[P] extends ReadonlyArray<infer U>
      ? ReadonlyArray<DeepPartial<U>>
      : DeepPartial<T[P]>
};

type DeepMerge<T, U> = T extends any ? U extends any ?
  [T, U] extends [object, object] ?
  {
    [K in keyof (U & Pick<T, Exclude<keyof T, keyof U>>)]: (
      K extends keyof U ? (
        K extends keyof T ? DeepMerge<T[K], U[K]> : U[K]
      ) : (
        K extends keyof T ? T[K] : never
      )
    )
  } : U : never : never;

Actualizare

type DeepMerge<T, U> = T extends any ? U extends DeepPartial<T> ?
  [T, U] extends [object, object] ?
  {
    [K in keyof (U & Pick<T, Exclude<keyof T, keyof U>>)]: (
      K extends keyof U ? (
        K extends keyof T ? DeepMerge<T[K], U[K]> : U[K]
      ) : (
        K extends keyof T ? T[K] : never
      )
    )
  } : U : never : never;

Acest lucru funcționează bine, dar nu limitează U să nu adauge proprietăți noi.