typescript - Miksi ts-toolbelt-kirjasto käyttää "O extends unknown" -lausetta
Opiskelen ts-toolbelt-kirjaston lähdekoodia. Ja usein törmään tähän ilmaisuun O extends unknown. Minun mielestäni se ei lisää mitään toiminnallisuutta.
Ja niin minä mietin, mihin sitä käytetään?
/**
* @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
Jostain syystä _UnionOf-tyypin viemisen sijaan sitä edeltää lauseke O extends unknown
Vastaus #1
Oletettavasti kirjaston kirjoittajat päättivät, että UnionOf<A | B | C> pitäisi tuottaa saman tuloksen kuin UnionOf<A> | UnionOf<B> | UnionOf<C>, ja _UnionOf:n määritelmä ei tehnyt niin. O extends unknown ? ... : never -tarkistus, joka harhaanjohtavasti näyttää siltä, ettei se tee mitään, aiheuttaa tämän tapahtumaan.
Lausekkeet, jotka näyttävät tekevän mitään, mutta jakautuvat itse asiassa liitoihin, kun T on geneerinen tyyppiparametri:
T extends unknown ? ... : neverT extends any ? ... : neverT extends T ? ... : never
Jos T on tyyppiparametri, kuten geneerissä funktiossa function foo<T>(/*...*/): void, geneerisessä tyyppialiasissa type Foo<T>=/*...*/ tai geneerisessä rajapinnassa interface Foo<T> {/*...*/}, niin tyyppi muodossa T extends XXX ? YYY : ZZZ on jakautuva ehdollinen tyyppi.
Se jakaa ehdollisen tarkistuksen liittojen kesken T:ssä. Kun T määritellään jollain tietyllä tyypillä, kääntäjä jakaa kyseisen tyypin sen liittojäseniksi, arvioi tarkistuksen jokaiselle tällaiselle jäsenelle ja yhdistää sitten tulokset takaisin uuteen liittoon. Joten jos F<T> jakautuu liittojen kesken, niin F<A | B | C> on sama kuin F<A> | F<B> | F<C>.
Kaikki tyypin funktiot eivät ole distributiivisia liitoksissa. Esimerkiksi keyof-operaattori ei muunna syötteiden liitoksia tulosten liitoksiksi:
type KeyofA = keyof {a: string}; // "a"
type KeyofB = keyof {b: number}; // "b"
type KeyofAorB = keyof ({ a: string } | { b: number }); // never
Samoin, _UnionOf ei ole distributiivinen unionien yli (liittyen siihen, että se käyttää määritelmässään keyof-avainsanaa):
type Oops = _UnionOf<{ a: string } | { b: number }>
// never
Jos sinulla on tyyppifunktio, joka ei ole unionien suhteen jakautuva, ja haluat sen olevan, voit kietoa sen jakautuvaan ehdolliseen tyyppiin:
type DistribKeyof<T> = T extends unknown ? keyof T : never;
type UnionKeyofAorB = DistribKeyof<{ a: string } | { b: number }>; // "a" | "b"
Ja siksi UnionOf on distributiivinen yhdistymien yli:
type Correct = UnionOf<{ a: string } | { b: number }>
// string | number
Leikkikenttälinkki koodiin
Vastaus #2
`O extends unknown ? _UnionOf<O> : never` on ehdotyyppi; tässä sitä käytetään jakautuvana ehdotyyppinä. Ilman ehdotyyppiä TypeScript kaventaisi tyypin yhdeksi, joka pätee kaikkiin mahdollisiin yhdistearvoihin (usein never). Jakautuvien ehdotyyppien avulla se soveltuu kuhunkin mahdolliseen yhdistearvoon. Vaikka jakautuvien ehdotyyppien ehdollista osaa voitaisiin käyttää mahdollisten yhdistearvojen suodattamiseen, tässä se ei ole tarpeen, joten näet usein arvoja kuten T extends any (kuten käsikirjassa) tai T extends unknown täällä.
Mitä tulee niiden väliseen eroon, dragomirtitian kuvaa sitä Typescriptin gitter.imissä:
anypoistaa joskus tietoa tyyppiparametrista. Tähän liittyen oli bugi, jonka itse raportoin: microsoft/TypeScript#30569. Sen pitäisi toimia nyt hyvin, mutta suosittelisin silti jotain kahta muuta tapaa [huom:T extends unknowntaiT extends T]. Mikä tahansa extends-lauseke ehdollisessa tyypissä lisää tietoaT:stä,extends anyvoi hämmentää kääntäjääT:n suhteen.
Katso myös:
- Mitä tarkoittaa, että tyyppi jakautuu liitoille?
- Typescriptin jakautuvat ehdolliset tyypit
