Eine Schritt-für-Schritt-Anleitung zum Schreiben eines generischen Typs in TypeScript, der beliebige verschachtelte Schlüsselwertstrukturen kombiniert.
Anmerkung des Übersetzers: Ich habe absichtlich einige Wörter (wie generisch, Schlüsselwert) nicht übersetzt, da dies meiner Meinung nach das Verständnis des Materials nur erschwert.
TLDR:
Der Quellcode für DeepMergeTwoTypes
befindet sich am Ende des Artikels. Kopieren Sie es in Ihre IDE, um damit zu spielen.
Wie es in vsCode aussieht:
, 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
Wie kann ich das DeepMergeTwoTypes<T, U>
Generikum so reparieren , dass es N
Argumente anstelle von zwei akzeptiert?
Ich werde dieses Zeug für den nächsten Artikel belassen, aber Sie können meinen Arbeitsentwurf hier sehen .
Anmerkung des Übersetzers
Dies ist meine erste Übersetzungserfahrung. Sie werden gebeten, eine persönliche Nachricht über Tippfehler, Kommas und einfach sprachgebundene Sätze zu schreiben.