Typescript에서 깊이 있게 병합하는 방법

Typescript deep merge 일반 유형을 만드는 방법에 대한 단계별 자습서입니다. 이 일반 유형은 일치하지 않는 키 값 구조에 적용됩니다.

TLDR:


DeepMergeTwoTypes 일반 소스 코드는 본문의 밑에 있습니다.
너는 그것을 복사해서 너의 IDE에 붙여서 그것을 가지고 놀 수 있다.
you can play with the code here
Or check the GitHub repo https://github.com/Svehla/TS_DeepMerge
type A = { key1: { a: { b: 'c' } }, key2: undefined }
type B = { key1: { a: {} }, key3: string }
type MergedAB = DeepMergeTwoTypes<A, B>

선결 조건


고급 typescript 유형을 깊이 연구하고 싶다면, 이 typescript 시리즈를 추천합니다. 그 안에 유용한 예가 많습니다.
기본 유형: (918) 추정 457)
  • 더 높은 모조약
  • 유형 스크립트 및 운영자 행동 문제


    먼저 Typescript 유형 병합 문제를 살펴보겠습니다.두 가지 유형AB, 그리고 새로운 유형MergedAB을 정의해 봅시다. 이것은 합병의 결과A & B입니다.
    type A = { key1: string, key2: string }
    type B = { key1: string, key3: string }
    
    type MergedAB = (A & B)['key1']
    

    일치하지 않는 데이터 형식을 통합하기 전에 모든 것이 좋아 보입니다.
    type A = { key1: string, key2: string }
    type B = { key2: null, key3: string }
    
    type MergedAB = (A & B)
    
    보시다시피 typeAkey2를 문자열로 정의하고 typeBkey2null 값으로 정의합니다.

    타입never과 타입MergedAB이 작업을 중지했을 때 Typescript는 이러한 일치하지 않는 타입 합병 문제를 해결했다.저희가 예상한 산출은 이렇습니다.
    type ExpectedType = {
      key1: string | null,
      key2: string,
      key3: string
    }
    

    차츰차츰 해결하다


    Typescript 유형을 반복적으로 심도 있게 통합하는 적절한 범주를 만듭니다.
    우선, 우리는 두 개의 조수 범주형 유형을 정의했다.

    GetObjDifferentKeys<>


    type GetObjDifferentKeys<
      T,
      U,
      T0 = Omit<T, keyof U> & Omit<U, keyof T>,
      T1 = {
        [K in keyof T0]: T0[K]
      }
     > = T1
    
    이 유형은 2개의 객체를 수락하고 새 객체를 반환합니다. 이 객체는 AB에 고유한 키만 포함됩니다.
    type A = { key1: string, key2: string }
    type B = { key2: null, key3: string }
    
    type DifferentKeysAB = (GetObjDifferentKeys<A, B>)['k']
    

    GetObjSameKeys<>


    이전의 범형과 반대로, 우리는 두 대상 중 같은 모든 관건을 픽업하는 새로운 범형을 정의할 것이다.
    type GetObjSameKeys<T, U> = Omit<T | U, keyof GetObjDifferentKeys<T, U>>
    
    되돌아오는 유형은 대상입니다.
    type A = { key1: string, key2: string }
    type B = { key2: null, key3: string }
    type SameKeys = GetObjSameKeys<A, B>
    

    모든 helpers 함수가 완성되었기 때문에mainDeepMergeTwoTypes 범위를 실현할 수 있습니다.

    두 가지 유형 깊이 병합<>


    type DeepMergeTwoTypes<
      T,
      U, 
      // non shared keys are optional
      T0 = Partial<GetObjDifferentKeys<T, U>>
        // shared keys are required
        & { [K in keyof GetObjSameKeys<T, U>]: T[K] | U[K] },
      T1 = { [K in keyof T0]: T0[K] }
    > = T1
    
    
    이 범용 검색 대상TU 사이의 모든 비공유 키는 Typescript에서 제공하는 Partial<> 범용으로 선택할 수 있는 키가 됩니다.& 연산자를 통해 선택할 수 있는 키가 있는 유형과 모든TU 공유 키(값T[K] | U[K] 유형을 포함하는 대상을 통합합니다.
    아래의 예에서 보듯이새 공통 키가 비공유 키를 찾았고 선택할 수 있는 키로 만들었습니다. 나머지 키는 엄격히 요구됩니다.
    type A = { key1: string, key2: string }
    type B = { key2: null, key3: string }
    type MergedAB = DeepMergeTwoTypes<A, B>
    

    그러나 현재의 ? 범형은 플러그인 구조 유형을 귀속적으로 처리할 수 없다.따라서 모든 플러그인 구조가 통합될 때까지 대상 통합 기능을 DeepMergeTwoTypes라는 새로운 범주에서 추출하고 MergeTwoObjects 다시 호출합니다.
    // this generic call recursively DeepMergeTwoTypes<>
    
    type MergeTwoObjects<
      T,
      U, 
      // non shared keys are optional
      T0 = Partial<GetObjDifferentKeys<T, U>>
      // shared keys are recursively resolved by `DeepMergeTwoTypes<...>`
      & {[K in keyof GetObjSameKeys<T, U>]: DeepMergeTwoTypes<T[K], U[K]>},
      T1 = { [K in keyof T0]: T0[K] }
    > = T1
    
    export type DeepMergeTwoTypes<T, U> =
      // check if generic types are arrays and unwrap it and do the recursion
      [T, U] extends [{ [key: string]: unknown}, { [key: string]: unknown } ]
        ? MergeTwoObjects<T, U>
        : T | U
    
    전문 알림:DeepMergeTwoTypes와if-else 조건에서 typeDeepMergeTwoTypesTtupleU에 통합하여 이 두 종류가 조건(javascript 조건과 유사한[T, U] 조작부호를 성공적으로 통과했는지 검증할 수 있음)
    이 범용형은 이 두 파라미터의 유형이 && (aka { [key: string]: unknown } 인지 검사합니다.만약 이것이 사실이라면, 그것은 Object 을 통해 그것들을 합병할 것이다.모든 중첩된 객체에 대해 반복합니다.
    보다🎉 현재 모든 플러그인 대상에 범용적으로 적용됩니다
    예:
    type A = { key: { a: null, c: string} }
    type B = { key: { a: string, b: string} }
    
    type MergedAB = DeepMergeTwoTypes<A, B>
    

    이게 다야?
    불행하게도, 우리의 새로운 범위는 그룹을 지원하지 않습니다.

    스토리지 지원 추가


    계속하기 전에 우리는 반드시 키워드MergeTwoObject<>를 알아야 한다.infer 데이터 구조를 검색하고 데이터 유형을 추출합니다(예: 추출된 배열의 데이터 유형). 여기서 infer 기능에 대한 자세한 내용을 확인할 수 있습니다.
    https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types

    다른 조수 범형을 정의합시다!


    추단

    Head<T> 이 범형은 하나의 그룹을 받아들여 첫 번째 항목으로 되돌려줍니다.
    type Head<T> = T extends [infer I, ...infer _Rest] ? I : never
    
    type T0 = Head<['x', 'y', 'z']>
    

    머리


    이 범형은 하나의 그룹을 받아들여 첫 번째 항목을 제외한 모든 항목을 되돌려줍니다.
    type Tail<T> = T extends [infer _I, ...infer Rest] ? Rest : never
    
    type T0 = Tail<['x', 'y', 'z']>
    

    이것이 바로 우리가 최종적으로 수조 합병을 실현하는 데 필요한 전부이다. 그러므로 우리가 그것을 해독합시다.

    꼬리<T>

    Zip_DeepMergeTwoTypes<T, U>는 간단한 귀속 범주형으로 항목 인덱스 위치에 따라 두 개의 그룹을 하나의 그룹으로 합친다.
    type Head<T> = T extends [infer I, ...infer _Rest] ? I : never
    type Tail<T> = T extends [infer _I, ...infer Rest] ? Rest : never
    
    type Zip_DeepMergeTwoTypes<T, U> = T extends []
      ? U
      : U extends []
      ? T
      : [
          DeepMergeTwoTypes<Head<T>, Head<U>>,
          ...Zip_DeepMergeTwoTypes<Tail<T>, Tail<U>>
      ]
    
    type T0 = Zip_DeepMergeTwoTypes<
      [
        { a: 'a', b: 'b'},
      ],
      [
        { a: 'aaaa', b: 'a', c: 'b'},
        { d: 'd', e: 'e', f: 'f' }
      ]
    >
    
    

    현재 우리는 Zip_DeepMergeTwoTypes 범주형에서 두 행장의 통합을 작성할 것이다. DeepMergeTwoTypes<T, U> 범주형으로 인해 압축값을 제공했다.
    export type DeepMergeTwoTypes<T, U> =
      // ----- 2 added lines ------
      // this line ⏬
      [T, U] extends [any[], any[]]
        // ... and this line ⏬
        ? Zip_DeepMergeTwoTypes<T, U>
        // check if generic types are objects
        : [T, U] extends [{ [key: string]: unknown}, { [key: string]: unknown } ]
          ? MergeTwoObjects<T, U>
          : T | U
    

    그리고이게 다야!!!🎉


    우리 해냈어!빈 값, 내포된 객체 및 긴 배열에 대해서도 값이 올바르게 결합됩니다.
    더 복잡한 데이터로 해보겠습니다.
    type A = { key1: { a: { b: 'c'} }, key2: undefined }
    type B = { key1: { a: {} }, key3: string }
    
    
    type MergedAB = DeepMergeTwoTypes<A, B>
    

    전체 소스 코드


    type Head<T> = T extends [infer I, ...infer _Rest] ? I : never
    type Tail<T> = T extends [infer _I, ...infer Rest] ? Rest : never
    
    type Zip_DeepMergeTwoTypes<T, U> = T extends []
      ? U
      : U extends []
      ? T
      : [
          DeepMergeTwoTypes<Head<T>, Head<U>>,
          ...Zip_DeepMergeTwoTypes<Tail<T>, Tail<U>>
      ]
    
    
    /**
     * Take two objects T and U and create the new one with uniq keys for T a U objectI
     * helper generic for `DeepMergeTwoTypes`
     */
    type GetObjDifferentKeys<
      T,
      U,
      T0 = Omit<T, keyof U> & Omit<U, keyof T>,
      T1 = { [K in keyof T0]: T0[K] }
     > = T1
    /**
     * Take two objects T and U and create the new one with the same objects keys
     * helper generic for `DeepMergeTwoTypes`
     */
    type GetObjSameKeys<T, U> = Omit<T | U, keyof GetObjDifferentKeys<T, U>>
    
    type MergeTwoObjects<
      T,
      U, 
      // non shared keys are optional
      T0 = Partial<GetObjDifferentKeys<T, U>>
      // shared keys are recursively resolved by `DeepMergeTwoTypes<...>`
      & {[K in keyof GetObjSameKeys<T, U>]: DeepMergeTwoTypes<T[K], U[K]>},
      T1 = { [K in keyof T0]: T0[K] }
    > = T1
    
    // it merge 2 static types and try to avoid of unnecessary options (`'`)
    export type DeepMergeTwoTypes<T, U> =
      // ----- 2 added lines ------
      [T, U] extends [any[], any[]]
        ? Zip_DeepMergeTwoTypes<T, U>
        // check if generic types are objects
        : [T, U] extends [{ [key: string]: unknown}, { [key: string]: unknown } ]
          ? MergeTwoObjects<T, U>
          : T | U
    
    
    you can play with the code here
    Or check the GitHub repo https://github.com/Svehla/TS_DeepMerge

    다음은요?


    Typescript 유형 시스템의 또 다른 고급 사용법에 관심이 있다면, 고급 Typescript 범주를 만드는 방법에 대한 단계별 글/강좌를 보십시오.




  • 🎉🎉🎉🎉🎉

    좋은 웹페이지 즐겨찾기