javascript - Miten lodash flow -funktio tulisi tyypittää Typescriptissä?
lodash.flow yhdistää kaksi tai useampia funktioita.
lodash.flow(double, addTwo) palauttaisi funktion, joka kaksinkertaistaa ja lisää kaksi. Kuinka tämä funktio pitäisi tyypittää TypeScriptissä (nykyinen määritelmä palauttaa vain Function)?
declare function flow<In, Intermediate, Out>(f1: (a1: In) => Intermediate, f2: (a1: Intermediate) => Out): (a1: In)=> Out toimii kahdelle funktiolle, joista ensimmäisellä on yksi syöteargumentti. En ole varma, kuinka laajentaa määritettä niin, että se toimisi kaikissa tapauksissa.
Yritykseni voi tukea useita toimintoja, jos sitä kutsutaan näin:
lodash.flow(f1, lodash.flow(f2, f3))mutta etsin
lodash.flow(f1, f2, f3)Vastaus #1
En usko, että voit kirjoittaa tuon määritelmän.
Jos katsomme lodashin tyyppimääritystiedostoa, he eivät yritä ilmaista sitä suhdetta.
interface LoDashStatic {
flow<TResult extends Function>(...funcs: Function[]): TResult;
}
Mutta se yksinään ei ole riittävä syy sulkea mahdollisuutta pois. Kirjoittajat ovat saattaneet vain jättää jotain huomiotta, joten jatketaan asian pohtimista.
Yksittäisen funktioketjun välistä suhdetta on jotain, jonka voit esittää. Olet tehnyt niin yllä olevassa esimerkissäsi. Voisit luoda manuaalisia versioita samasta ideasta useille parametripituuksille, mutta se johtuu siitä, että luot tilanteen, jossa ketjun pituus on tiedossa ja voit antaa yksittäistä tyyppi-informaatiota.
Jos meidän on käsiteltävä muuttuvan pituuden parametrien tapausta, meidän on kohdeltava parametreja kuin Kokoelmaa. Koska kaikilla muuttujilla on oltava yksi (vaikkakin mahdollisesti parametrisoitu) tyyppi, myös tämän kokoelmaolion on oltava tällainen. Kuitenkin erilaisten funktioiden tyypit eivät ole yhdenmukaisia. (param:A) => B ei ole samaa tyyppiä kuin (param:B) => C eikä sitä voida säilyttää samassa hyvin tyypitetyssä säiliössä (lukuun ottamatta yhdistetytyyppejä, mutta nekaan eivät skaalaudu).
Tilanteissa, joissa haluat säilyttää tyyppitiedon tällaisten parametrilistojen kohdalla, määrittelet yleensä koostefunktion kahdelle parametrille ja sovellat sitä useiden funktioiden yli. Näin tyyppitieto säilyy esimerkiksi lupauksissa (promises). Jotta tämä toimisi, jokainen yksittäinen parametri täytyy silti kirjoittaa erikseen. Se vain varmistaa, että lopputuloksena sinulla on oikea tulostyyppi. Sanottakoon kuitenkin, että useimmissa tapauksissa tämä on juuri sitä, mitä haluat, joten kaikki on kunnossa.
Jos lodash olisi kirjoitettu hyvin tyypitetyllä funktionaalisella kielellä, tuo flow-funktio ei todennäköisesti olisi olemassa. Kuvittelen, että se olisi sen sijaan kirjoitettu putkitetun koostumusobjektin muodossa.
PÄIVITYS: Mitä tarkoitan, kun sanon 'putkitetulla koostumusobjektilla'? Jotain tällaista, ehkä:
class FunctionComposer<T,V> {
constructor(protected func: (param: T) => V) { }
public compose<X>(newFunc: (param:V) => X) {
return new FunctionComposer((x: T) => newFunc(this.func(x)));
}
public out() {
return this.func;
}
}
let composedFunc = new FunctionComposer((x: number) => x * 2)
.compose(x => x.toString())
.out();
// composedFunc has type (param:number) => string
Vastaus #2
tässä on tapa tehdä se eräänlaisesti käyttäen ehdollisia tyyppejä:
const compose =
<T1, T2>(f1: (p: T1) => T2) =>
<T3>(f2: (p: T2) => T3) =>
(p: T1) =>
f2(f1(p));
const flow = <T1, T2, T3 = "♘", T4 = "♘">(
f0: (p: T1) => T2,
...f: [] | [(p: T2) => T3] | [(p: T2) => T3, (p: T3) => T4]
): ((p: T1) => T3 extends "♘" ? T2 : T4 extends "♘" ? T3 : T4) => {
if (f[1]) {
return compose(compose(f0)(f[0]!))(f[1]) as any;
}
if (f[0]) {
return compose(f0)(f[0]) as any;
}
return f0 as any;
};
tässä esimerkissä flow tukee enintään 3 argumenttia, mutta sitä voidaan laajentaa niin monella kuin haluat.
