parsing - Typescript nu înțelege genericul
Încep să învăț despre TypeScript și am încercat să fac o bibliotecă de combinatori de parser, la început tipurile și genericele erau ușoare, dar a apărut o problemă.
// The generic T represents the value the parser returns
class Parser<T> {
...
}
// This function combines the two parsers in one tuple
function pair<P1 extends Parser<A>, P2 extends Parser<B>, A, B>(
parser1: Parser<A>,
parser2: Parser<B>
): Parser<{ fst: A, snd: B }> {
...
}
// Maps a Parser<A> to a Parser<B> through A => B
function map<P extends Parser<A>, F extends (a: A) => B, A, B>(
parser: P, mapFn: F
): Parser<B> {
...
}
Iată unde am problema:
// Like pair but the value of the parser is A, instead of {fst: A, snd: B}
function left<P1 extends Parser<A>, P2 extends Parser<B>, A, B>(
parser1: P1,
parser2: P2
): Parser<A> {
return map(pair(parser1, parser2), (a: unknown): A => (a as {fst: R1, snd: R2}).fs );
}
Funcția `left` furnizată se compilează, dar nu înțeleg de ce TypeScript nu poate deduce că `unknown` ar trebui să fie `{fst: A, snd: B}`, este aceasta o problemă a TypeScript sau doar eu greșesc cum funcționează genericele în TypeScript.
Mulțumesc anticipat.
EDITARE
Am adăugat un spațiu de lucru reproductibil:
Iată linkul reproductibil
Răspuns #1
Tot ce trebuie să faci este să deduci în mod explicit genericul Parser și să mapezi tipul de retur al callback-ului.
Exemplu :
// The generic T represents the value the parser returns
class Parser<T> { }
// This function combines the two parsers in one tuple
function pair<P1 extends Parser<A>, P2 extends Parser<B>, A, B>(
parser1: Parser<A>,
parser2: Parser<B>
): Parser<{ fst: A, snd: B }> {
return null as any
}
type InferGeneric<T extends Parser<unknown>> = T extends Parser<infer R> ? R : never
function map<P extends Parser<A>, F extends (a: /** explicit infer generic */ InferGeneric<P>) => B, A, B>(
parser: P, mapFn: F
): Parser<ReturnType<F>> /** Explicit ReturnType infer */ {
return null as any
}
// Like pair but the value of the parser is A, instead of {fst: A, snd: B}
function left<P1 extends Parser<A>, P2 extends Parser<B>, A, B>(
parser1: P1,
parser2: P2
): Parser<A> {
return map(pair(parser1, parser2), a => 42); // ok
}
Teren de joacă
Răspunsul #2
Folosește mai puține generice
Când ai parametri de tip generici care sunt deduși ca unknown, unul dintre cele mai bune lucruri pe care le poți face este să reduci numărul de parametri de tip generici ai funcției tale și, în schimb, să derive anumite tipuri din altele.
Uneori, acest lucru implică utilizarea tipurilor de utilitate încorporate, cum ar fi ReturnType<T>, pentru a obține tipul de returnare din tipul unei funcții T. De asemenea, putem defini propriile noastre tipuri de utilitate folosind o condiție împreună cu infer/extends.
În exemplul tău există, de fapt, o soluție foarte simplă în care A și B sunt singurele valori generice.
class Wrapper<T> {
callable: () => T;
constructor(callable: () => T) {
this.callable = callable;
}
}
function map<A, B>(w: Wrapper<A>, mapFn: (a: A) => B): Wrapper<B> {
return new Wrapper(() => mapFn(w.callable()));
}
function pair<A, B>(w1: Wrapper<A>, w2: Wrapper<B>): Wrapper<{fst: A, snd: B}> {
return new Wrapper(() => ({fst: w1.callable(), snd: w2.callable()}));
}
function left<A, B>(w1: Wrapper<A>, w2: Wrapper<B>): Wrapper<A> {
return map(pair(w1, w2), value => value.fst);
}
Link către Playground TypeScript
