Un didacticiel pas à pas sur la façon d'écrire un type générique dans TypeScript qui combine des structures clé-valeur imbriquées arbitraires.
Note du traducteur: je n'ai pas traduit intentionnellement certains mots (comme générique, valeur-clé), car, à mon avis, cela ne fera que compliquer la compréhension du matériel.
TLDR:
Le code source de DeepMergeTwoTypes
sera à la fin de l'article. Copiez-le dans votre IDE pour jouer avec.
À quoi ça ressemble dans 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
Comment puis-je corriger le DeepMergeTwoTypes<T, U>
générique afin qu'il puisse prendre des N
arguments au lieu de deux?
Je vais laisser ces choses pour le prochain article, mais vous pouvez voir mon brouillon de travail ici ).
Note du traducteur
C'est ma première expérience de traduction. Vous êtes prié d'écrire dans un message personnel sur les fautes de frappe, les virgules et les phrases simplement liées à la langue.