Ketikan: Menggabungkan tipe secara mendalam

Tutorial langkah demi langkah tentang cara menulis tipe generik di TypeScript yang menggabungkan struktur nilai kunci bertingkat arbitrer.

Catatan penerjemah: Saya sengaja tidak menerjemahkan beberapa kata (seperti generic, key-value), karena menurut saya hal ini hanya akan mempersulit pemahaman materi.

TLDR:

Kode sumber untuk DeepMergeTwoTypesakan ada di akhir artikel. Salin ke IDE Anda untuk dimainkan.

Bagaimana tampilannya di 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

Bagaimana cara memperbaiki  DeepMergeTwoTypes<T, U> generik sehingga bisa mengambil  N argumen, bukan dua?

Saya akan meninggalkan hal ini untuk artikel berikutnya, tetapi Anda dapat melihat draf kerja saya di sini ).

Catatan penerjemah

Ini adalah pengalaman terjemahan pertama saya. Anda dengan hormat diminta untuk menulis secara pribadi tentang kesalahan ketik, koma, dan frasa yang diikat lidah.




All Articles