typescript - Parameters permite ambalarea unei funcții generice, dar infer nu


Typescript aruncă o eroare atunci când folosește inferența pentru un parametru de funcție, când nu o face pentru tipul utilitar Parameters. Luați următorul exemplu: [playground]

interface Filter<T> {
    (inp: T): boolean;
}

// get parameter using Parameters
type InpParam<F extends Filter<never>> = Parameters<F>[0];
// get parameter using infer
type InpInfer<F extends Filter<never>> = F extends Filter<infer T> ? T : never;

// compiles fine
function wrapParam<F extends Filter<never>>(filt: F): Filter<InpParam<F>> {
    const wrapper = (inp: InpParam<F>): boolean => filt(inp);
    return wrapper;
}

function wrapInfer<F extends Filter<never>>(filt: F): Filter<InpInfer<F>> {
    const wrapper = (inp: InpInfer<F>): boolean => filt(inp);
    //                                                  ^^^
    // argument of type InpInfer<F> is not assignable to type never
    return wrapper;
}

Nu sunt exact sigur de ce InpParam poate fi atribuit lui never, de exemplu este inferat ca never, în timp ce InpInfer nu este și este în schimb inferat ca unknown. Ar putea avea legătură cu modul în care gestionează Parameters goale, dar aici Parameters returnează de fapt lucrul greșit, returnează undefined în loc de unknown. (prin greșit, vreau să spun că unknown este un parametru de intrare mai generic, deoarece parametrii funcției sunt contravarianți).

Sunt curios să aflu cum să rezolv asta din două motive:

  1. undefined este un tip prea îngust, tipul real ar trebui să fie unknown. Aceasta prezintă o problemă particulară atunci când se intersectează cu un alt tip, deoarece unknown & T => T, dar undefined & {} => never.
  2. Pentru tipuri de intrări complicate, scrierea expresiei pentru a extrage un tip imbricat poate fi complicată, greu de citit și fragilă la schimbările definițiilor obiectelor. Infer însă funcționează întotdeauna atâta timp cât genericele au aceleași semnificații.

Note

  • Știu că wrapper-ele specifice în cauză pot fi scrise făcând wrapper-ul generic în parametrul de intrare, însă acesta nu este cazul dacă vreau să păstrez și tipul filtrului în sine (aceste exemple pur și simplu nu ilustrează asta pentru claritate).
  • Știu, de asemenea, că ambalajele specifice de aici nu fac nimic și, prin urmare, ar putea fi omise.