La funzione tapAsync di Typescript non è in grado di impostare digitazioni corrette
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 x
valore x => fn(x)
, quindi concatenata per restituire il valore restituito y
o il valore tappatox
x => fn(x).then(y => y || x)
Questa prima versione che utilizza il any
tipo 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, X
per l'argomento iniziale e Y
per 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
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||X
non essere in grado di dedurre che non potrà mai restituire un vuoto se la X iniziale non era un vuoto.