Typographie: combinaison de types en profondeur

Un didacticiel pas à pas sur la façon d'écrire un type générique dans TypeScript qui combine des structures clé-valeur imbriquées arbitraires.

Note du traducteur: je n'ai pas traduit intentionnellement certains mots (comme générique, valeur-clé), car, à mon avis, cela ne fera que compliquer la compréhension du matériel.

TLDR:

Le code source de DeepMergeTwoTypessera à la fin de l'article. Copiez-le dans votre IDE pour jouer avec.

À quoi ça ressemble dans vsCode:

, generic- TypeScript, (Miniminalist Typescript - Generics)

IDE (. : TypeScript Playground ).

Disclaimer

production ( , ).

&- Typescript

. A B C, A & B

type A = { key1: string, key2: string }
type B = { key2: string, key3: string }
type C = A & B
const a = (c: C) => c.

, .

type A = { key1: string, key2: string }
type B = { key2: null, key3: string }
type C = A & B

A key2 , B null.

Typescript never C . - :

type ExpectedType = {
  key1: string | null,
  key2: string,
  key3: string
}

generic-, Typescript. 2 generic-.

GetObjDifferentKeys<>

type GetObjDifferentKeys<T, U> = Omit<T, keyof U> & Omit<U, keyof T>

2 , A B.

type A = { key1: string, key2: string }
type B = { key2: null, key3: string }

type C = GetObjDifferentKeys<A, B>['']

GetObjSameKeys<>

generic- , , .

type GetObjSameKeys<T, U> = Omit<T | U, keyof GetObjDifferentKeys<T, U>>

— .

type A = { key1: string, key2: string }
type B = { key2: null, key3: string }
type C = GetObjSameKeys<A, B>

, generic- DeepMergeTwoTypes

DeepMergeTwoTypes<>

type DeepMergeTwoTypes<T, U> =
  // " " ()  - 
  Partial<GetObjDifferentKeys<T, U>>
  //   - 
  & { [K in keyof GetObjSameKeys<T, U>]: T[K] | U[K] }

generic " " T U, (). Partial<>, Typescript. ( &-) T U , T[K] | U[K].

. generic "-" (?), .

type A = { key1: string, key2: string }
type B = { key2: null, key3: string }
const fn = (c: DeepMergeTwoTypes<A, B>) => c.

 DeepMergeTwoTypes generic . generic  MergeTwoObjects   DeepMergeTwoTypes  , .

//  generic   DeepMergeTwoTypes<>
type MergeTwoObjects<T, U> =
  // " " ()  - 
  Partial<GetObjDifferentKeys<T, U>>
  //   - 
  & {[K in keyof GetObjSameKeys<T, U>]: DeepMergeTwoTypes<T[K], U[K]>}

export type DeepMergeTwoTypes<T, U> =
  //     ,    
  [T, U] extends [{ [key: string]: unknown }, { [key: string]: unknown } ]
    ? MergeTwoObjects<T, U>
    : T | U

PRO TIP: , DeepMergeTwoTypes if-else (extends ?:) T U , (tuple) [T, U]. &&- Javascript.

generic ,  { [key: string]: unknown } ( Object). ,  MergeTwoObject<>. .

: extends { [key: string]: unknown } -, .. , , booleans ...

! generic . :

type A = { key: { a: null, c: string} }
type B = { key: { a: string, b: string} }
const fn = (c: MergeTwoObjects<A, B>) => c.key.

?

, . generic .

, , infer (to infer - ).

infer  ( ).  infer  (Type inference in conditional types).

infer. (Item):

export type ArrayElement<A> = A extends (infer T)[] ? T : never

// Item === (number | string)
type Item = ArrayElement<(number | string)[]>

, , .  DeepMergeTwoTypes  .

export type DeepMergeTwoTypes<T, U> =
  // ----- 2   ------
  //  ⏬
  [T, U] extends [(infer TItem)[], (infer UItem)[]]
    // ...   ⏬
    ? DeepMergeTwoTypes<TItem, UItem>[]
    : ... rest of previous generic ...

DeepMergeTwoTypes , .

type A = [{ key1: string, key2: string }]
type B = [{ key2: null, key3: string }]
const fn = (c: DeepMergeTwoTypes<A, B>) => c[0].

! ?

... . Nullable non-nullable.

type A = { key1: string }
type B = { key1: undefined }

type C = DeepMergeTwoTypes<A, B>['key']

string | undefined, . if-else .

export type DeepMergeTwoTypes<T, U> =
  [T, U] extends [(infer TItem)[], (infer UItem)[]]
    ? DeepMergeTwoTypes<TItem, UItem>[]
    : [T, U] extends [{ [key: string]: unknown}, { [key: string]: unknown } ]
      ? MergeTwoObjects<T, U>
      // ----- 2   ------
      //  ⏬
      : [T, U] extends [
          { [key: string]: unknown } | undefined, 
          { [key: string]: unknown } | undefined 
        ]
        // ...   ⏬
        ? MergeTwoObjects<NonNullable<T>, NonNullable<U>> | undefined
          : T | U

nullable :

type A = { key1: string }
type B = { key1: undefined }


const fn = (c: DeepMergeTwoTypes<A, B>) => c.key1;

... !

! nullable , .

generic :

type A = { key1: { a: { b: 'c'} }, key2: undefined }
type B = { key1: { a: {} }, key3: string }


const fn = (c: DeepMergeTwoTypes<A, B>) => c.

:

/**
 *  2  T  U    ,   
 * .   `DeepMergeTwoTypes`
 */
type GetObjDifferentKeys<T, U> = Omit<T, keyof U> & Omit<U, keyof T>
/**
 *  2  T and U       
 *   `DeepMergeTwoTypes`
 */
type GetObjSameKeys<T, U> = Omit<T | U, keyof GetObjDifferentKeys<T, U>>
type MergeTwoObjects<T, U> =
  // " "  
  Partial<GetObjDifferentKeys<T, U>>
  //       `DeepMergeTwoTypes<...>`
  & { [K in keyof GetObjSameKeys<T, U>]: DeepMergeTwoTypes<T[K], U[K]> }

//  2 
export type DeepMergeTwoTypes<T, U> =
  //     ,    
  //  
  [T, U] extends [(infer TItem)[], (infer UItem)[]]
    ? DeepMergeTwoTypes<TItem, UItem>[]
    //    
    : [T, U] extends [
         { [key: string]: unknown}, 
         { [key: string]: unknown } 
      ]
      ? MergeTwoObjects<T, U>
      : [T, U] extends [
          { [key: string]: unknown } | undefined, 
          { [key: string]: unknown } | undefined 
        ]
        ? MergeTwoObjects<NonNullable<T>, NonNullable<U>> | undefined
          : T | U

// :
type A = { key1: { a: { b: 'c'} }, key2: undefined }
type B = { key1: { a: {} }, key3: string }

const fn = (c: DeepMergeTwoTypes<A, B>) => c.key

Comment puis-je corriger le  DeepMergeTwoTypes<T, U> générique afin qu'il puisse prendre des  N arguments au lieu de deux?

Je vais laisser ces choses pour le prochain article, mais vous pouvez voir mon brouillon de travail ici ).

Note du traducteur

C'est ma première expérience de traduction. Vous êtes prié d'écrire dans un message personnel sur les fautes de frappe, les virgules et les phrases simplement liées à la langue.




All Articles