Typescript에서 깊이 있게 병합하는 방법
41463 단어 advancedgenericstypescriptjavascript
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 유형 병합 문제를 살펴보겠습니다.두 가지 유형
A
과 B
, 그리고 새로운 유형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)
보시다시피 typeA
은 key2
를 문자열로 정의하고 typeB
은 key2
를 null
값으로 정의합니다.타입
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개의 객체를 수락하고 새 객체를 반환합니다. 이 객체는 A
및 B
에 고유한 키만 포함됩니다.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 함수가 완성되었기 때문에main
DeepMergeTwoTypes
범위를 실현할 수 있습니다.두 가지 유형 깊이 병합<>
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
이 범용 검색 대상T
과 U
사이의 모든 비공유 키는 Typescript에서 제공하는 Partial<>
범용으로 선택할 수 있는 키가 됩니다.&
연산자를 통해 선택할 수 있는 키가 있는 유형과 모든T
및 U
공유 키(값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 조건에서 typeDeepMergeTwoTypes
과T
tupleU
에 통합하여 이 두 종류가 조건(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 범주를 만드는 방법에 대한 단계별 글/강좌를 보십시오.
Reference
이 문제에 관하여(Typescript에서 깊이 있게 병합하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/svehla/typescript-how-to-deep-merge-170c텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)