打字稿:深入组合类型

有关如何在TypeScript中编写通用类型的分步教程,该通用类型结合了任意嵌套的键值结构。

译者注:我有意不翻译某些单词(例如通用,键值),因为我认为这只会使对材料的理解复杂化。

TLDR:

的源代码DeepMergeTwoTypes将在本文的结尾。将其复制到您的IDE即可使用。

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

我该如何修复 DeepMergeTwoTypes<T, U> 泛型,以便它可以接受 N 参数而不是两个?

我将把这些内容留给下一篇文章,但您可以在此处查看我的工作草案

译者注

这是我的第一次翻译经验。恳请您写关于错别字,逗号和简单用舌头打成的短语的个人信息。




All Articles