javascript - Typescript funkcia tapAsync nedokáže nastaviť správne typy


Chcem byť schopný zostaviť niekoľko ES6 asynchrónnych funkcií pomocou TypeScriptu. Funkcia tap by mala vrátiť argument, ak z tapovanej funkcie nič nie je vrátené, ale tapovať vrátenú hodnotu, ak niečo je vrátené z tapovanej funkcie.

príklad TypeScript ihriska

Dokážem to rozbehnúť bez písania typov, ale mám problémy s nastavovaním typov. Pozrite si ukážku pre príklad kódu fungujúceho v Javascript.

Funkcia tapovaná sa jednoducho volá s hodnotou x x => fn(x), potom sa reťazí, aby vrátila buď návratovú hodnotu y alebo tapovanú hodnotu x x => fn(x).then(y => y || x)

Táto prvá verzia používajúca typ any funguje, ale dostávam chyby, keď sa špecifikujem na spracovanie typov vo funkciách, ktoré sú tapped.

const tapAsync = (fn: (x: any) => Promise<any>) => (
  x: any
): Promise<any> => fn(x).then((y: any) => y || x)

Aby som bol konkrétnejší, používam dve generiká, X pre počiatočný argument a Y pre navrátenú hodnotu odchytenej funkcie.

const tapAsync = (fn: <X>(x: X) => Promise<X>) => (
  x: X
): Promise<Y|X> => fn(x).then(<Y>(y: Y) => y || x)

Keď volám funkcie pomocou tapAsync, dostanem nasledujúcu chybu.

src/index.ts:45:18 - error TS2345: Argument of type '({ foo }: { foo: any; }) => Promise<void>' is not assignable to parameter of type '<X>(x: X) => Promise<X>'.
  Types of parameters '__0' and 'x' are incompatible.
    Type 'X' is not assignable to type '{ foo: any; }'.

45   .then(tapAsync(one))
                    ~~~
src/index.ts:46:18 - error TS2345: Argument of type '({ foo }: { foo: any; }) => Promise<{ foo: any; bar: string; }>' is not assignable to parameter of type '<X>(x: X) => Promise<X>'.
  Types of parameters '__0' and 'x' are incompatible.
    Type 'X' is not assignable to type '{ foo: any; }'.

46   .then(tapAsync(two))
                    ~~~
src/index.ts:47:18 - error TS2345: Argument of type '({ foo, bar }: { foo: any; bar: any; }) => Promise<void>' is not assignable to parameter of type '<X>(x: X) => Promise<X>'.
  Types of parameters '__0' and 'x' are incompatible.
    Type 'X' is not assignable to type '{ foo: any; bar: any; }'.

47   .then(tapAsync(three))

V Typescripte nenastavujem žiadne typy na tapnutých funkciách, ale skúšal som použiť generické typy na druhej funkcii dva, ale ani to nefunguje

async function two<X>({ foo }): Promise<X> {
  console.log('two', foo)
  return {
    foo,
    bar: 'bar'
  }
}

async function one({ foo }) {
  console.log('one', foo)
}

async function two({ foo }) {
  console.log('two', foo)
  return {
    foo,
    bar: 'bar'
  }
}

async function three({ foo, bar }) {
  console.log('three', foo, bar)
}

const tapAsync = fn => x => fn(x).then(y => y || x)

Promise.resolve({ foo: 'foo' })
  .then(tapAsync(one))
  .then(tapAsync(two))
  .then(tapAsync(three))

Ďakujem za akúkoľvek pomoc!

============== úprava 2020-09-01 ====================

Hrám sa s kódom a trochu som rozpracoval typy, ale teraz dostávam chybu pri funkcii two, keď vracia nový objekt, aj keď má rovnaký tvar.

nový príklad prostredia TypeScript

const tapAsync = <X, Y>(fn: (x: X) => Promise<Y|void>) => 
  (x: X): Promise<X|Y> => 
    fn(x).then((y: Y|void) => y || x)

Odpoveď č. 1

Myslím, že s preťažením to funguje. A odvodzovanie typov vyzerá správne. V podstate to umožňuje kompilátoru TypeScriptu odvodiť, že nikdy neexistuje návrat typu (x:X)=>Promise<void | Y>. K tomu došlo preto, že po prvom volaní asynchrónnej funkcie, ktorá nič nevracia function :Promise<void>, bol typ Y odvodzený ako tento typ, a tak by Promise.then(... v ďalšom volaní pokúsil použiť X ako void | Args, čo by viedlo k (x: void | Args)=>...., čo je nekompatibilné s očakávaným (x: Args)=>....

Skontrolujte toto ihrisko váš príklad s preťažením

function tapAsync <X,Y>(fn: (x:X)=>Promise<void>): (x:X)=>Promise<X>;
function tapAsync <X,Y>(fn: (x:X)=>Promise<Y>): (x:X)=>Promise<Y>;
function tapAsync <X, Y>(fn: (x: X) => Promise<Y|void>) {
  return (x: X) => fn(x).then(y => y||x )
}

Upraviť: Pri opätovnom čítaní mojej odpovede som zabudol spomenúť, že problém spôsobuje (y: void| Y )=> Y||X, ktorý nedokáže odvodiť, že nikdy nemôže vrátiť void, ak pôvodné X nebolo void.