typescript - Prečo knižnica ts-toolbelt používa výraz "O extends unknown"
Študujem zdrojový kód knižnice ts-toolbelt. A často sa stretávam s týmto výrazom O extends unknown. Podľa môjho názoru to nepridáva žiadnu funkcionalitu.
A tak som rozmýšľal, na čo to je?
/**
* @hidden
*/
export type _UnionOf<O extends object> =
O[keyof O]
/**
* Transform an [[Object]] into an [[Union]]
* @param O to transform
* @returns [[Any]]
* @example
* ```ts
* ```
*/
export type UnionOf<O extends object> =
O extends unknown
? _UnionOf<O>
: never
Z nejakého dôvodu je namiesto exportovania typu _UnionOf pred ním výraz O extends unknown
Odpoveď č. 1
Predpokladá sa, že autori knižnice sa rozhodli, že UnionOf<A | B | C> by malo produkovať rovnaký výsledok ako UnionOf<A> | UnionOf<B> | UnionOf<C>, a definícia _UnionOf to nerobila. Kontrola O extends unknown ? ... : never, ktorá klamlivo vyzerá, že nič nerobí, spôsobuje, že sa to stane.
Výrazy, ktoré vyzerajú, že nič nerobia, ale v skutočnosti sa rozkladajú cez unie, keď je T generický typový parameter:
T extends unknown ? ... : neverT extends any ? ... : neverT extends T ? ... : never
Ak je T typovým parametrom, ako vo všeobecnej funkcii function foo<T>(/*...*/): void, všeobecnom typovom aliasi type Foo<T>=/*...*/ alebo všeobecnom rozhraní interface Foo<T> {/*...*/}, potom typ v tvare T extends XXX ? YYY : ZZZ je distribučný podmienený typ.
Rozdeľuje podmienenú kontrolu naprieč zlučeniami v T. Keď je T špecifikované nejakým konkrétnym typom, kompilátor rozdelí tento typ na jeho členov zlučenia, vyhodnotí kontrolu pre každého takého člena a potom spojí výsledky späť do nového zlučenia. Takže ak F<T> distribuuje naprieč zlučeniami, potom F<A | B | C> bude rovnaké ako F<A> | F<B> | F<C>.
Nie všetky typové funkcie sú distribuovateľné cez zjednotenia. Napríklad operátor keyof neprenáša zjednotenia vstupov do zjednotení výstupov:
type KeyofA = keyof {a: string}; // "a"
type KeyofB = keyof {b: number}; // "b"
type KeyofAorB = keyof ({ a: string } | { b: number }); // never
Podobne, _UnionOf nie je distribuované naprieč zjednoteniami (súvisiace s tým, že vo svojej definícii používa keyof):
type Oops = _UnionOf<{ a: string } | { b: number }>
// never
Ak máte typovú funkciu, ktorá nie je distribuovaná v zjednoceniach a chcete, aby bola, môžete ju zabaliť do distribuovaného podmieneného typu:
type DistribKeyof<T> = T extends unknown ? keyof T : never;
type UnionKeyofAorB = DistribKeyof<{ a: string } | { b: number }>; // "a" | "b"
A preto je UnionOf distributívna cez zjednotenia:
type Correct = UnionOf<{ a: string } | { b: number }>
// string | number
Odkaz na kód na ihrisku
Odpoveď č. 2
O extends unknown ? _UnionOf<O> : never je podmienený typ; tu sa používa ako distribučný podmienený typ. Bez použitia podmieneného typu TypeScript zúži typ na taký, ktorý platí pre všetky možné hodnoty zjednotenia (často never). Pri distribučných podmienených typoch sa uplatní na každú potenciálnu hodnotu zjednotenia. Hoci by sa podmienený aspekt distribučných podmienených typov mohol použiť na filtrovanie potenciálnych hodnôt zjednotenia, tu to nie je potrebné, preto často uvidíte hodnoty ako T extends any (ako v príručke) alebo T extends unknown tu.
Pokiaľ ide o rozdiel medzi nimi, dragomirtitian ho popisuje na gitter.im pre Typescript:
anyniekedy odstraňuje informácie o parametroch typu. Bol tu bug, ktorý som sám nahlásil microsoft/TypeScript#30569. Teraz by to malo fungovať v poriadku, ale stále by som odporúčal jeden z týchto dvoch spôsobov [poznámka:T extends unknownaleboT extends T]. Akékoľvek klauzuly extends v podmienenom type zavádzajú informácie oT,extends anymôže zmiasť kompilátor ohľadomT
Pozri tiež:
- Čo to znamená, keď sa typ rozkladá cez zjednotenia?
- TypeScript distribučné podmienené typy
