Um tutorial passo a passo sobre como escrever um tipo genérico no TypeScript que combina estruturas de valor-chave aninhadas arbitrárias.
Nota do tradutor: intencionalmente não traduzi algumas palavras (como genérico, valor-chave), pois, na minha opinião, isso só vai complicar a compreensão do material.
TLDR:
O código-fonte do DeepMergeTwoTypesestará no final do artigo. Copie-o para o seu IDE para brincar com ele.
Como fica no 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 | UPRO 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.keyComo posso corrigir o DeepMergeTwoTypes<T, U> genérico para que ele receba N argumentos em vez de dois?
Vou deixar isso para o próximo artigo, mas você pode ver meu rascunho de trabalho aqui ).
Nota do tradutor
Esta é minha primeira experiência de tradução. Solicita-se que você escreva uma mensagem pessoal sobre erros de digitação, vírgulas e frases simples.