La funzione tapAsync di Typescript non è in grado di impostare digitazioni corrette

Aug 24 2020

Voglio essere in grado di toccare insieme alcune funzioni asincrone ES6 usando il dattiloscritto. La funzione Tap dovrebbe restituire l'argomento se non viene restituito nulla dalla funzione TAP, ma toccare il valore restituito se viene restituito qualcosa da una funzione TAP.

esempio di parco giochi dattiloscritto

Posso farlo funzionare senza digitare, ma ho problemi a impostare i tipi. Consulta lo snippet per un esempio del codice che funziona in Javascript.

La funzione tappata viene semplicemente chiamata con il xvalore x => fn(x), quindi concatenata per restituire il valore restituito yo il valore tappatox x => fn(x).then(y => y || x)

Questa prima versione che utilizza il anytipo funziona, ma ricevo errori quando divento specifico sulla gestione dei tipi nelle funzioni toccate.

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

Per essere più specifico sto usando due generici, Xper l'argomento iniziale e Yper il valore restituito della funzione tappata.

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

Quando chiamo le funzioni usando tapAsync ottengo il seguente errore.

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))

Non sto impostando alcun tipo in dattiloscritto sulle funzioni toccate, ma ho provato a utilizzare tipi generici sulla seconda funzione due ma anche questo non funziona

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))

Grazie per qualsiasi aiuto!

============== modifica 2020-09-01 ====================

Ho giocato con il codice e ho arricchito un po' di più i tipi, ma ora ricevo un errore sulla funzione two quando restituisce un nuovo oggetto anche se ha la stessa forma.

nuovo esempio di parco giochi dattiloscritto

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

Risposte

1 NunoSousa Sep 01 2020 at 09:45

Penso che con i sovraccarichi funzioni. E l'inferenza del tipo sembra corretta. Fondamentalmente questo consente al compilatore ts di dedurre che non c'è mai un (x:X)=>Promise<void | Y>as ritorno. Il che è successo, perché dopo la prima chiamata a un async non restituito function :Promise<void> Y è stato dedotto essere di quel tipo, e quindi Promise.then(...avrebbe cercato di alimentare X come void | Args nella chiamata successiva, risultando in un (x: void | Args)=>....che è incompatibile con (x: Args)=>...previsto.

Controlla questo parco giochi il tuo esempio con i sovraccarichi

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 )
}

Modifica: rileggendo la mia risposta, non ho menzionato, il colpevole è il (y: void| Y )=> Y||Xnon essere in grado di dedurre che non potrà mai restituire un vuoto se la X iniziale non era un vuoto.