I am interested in connecting multiple ES6 async functions using TypeScript and the tap function. The tap function should return the argument if no value is returned from the tapped function, but return the tapped value if there is a return value.
I have managed to make it work without specifying types, but I am struggling with setting the correct types. Please refer to the snippet below for a working example of the code in JavaScript.
The tapped function is called simply with the x
value x => fn(x)
, then chained to either return the value y
or the tapped value x
x => fn(x).then(y => y || x)
This initial version using the any
type works, but when I try to specify types in the tapped functions, I encounter errors.
const tapAsync = (fn: (x: any) => Promise<any>) => (
x: any
): Promise<any> => fn(x).then((y: any) => y || x)
To be more specific, I am using two generics, X
for the initial argument, and Y
for the returned value of the tapped function.
const tapAsync = (fn: <X>(x: X) => Promise<X>) => (
x: X
): Promise<Y|X> => fn(x).then(<Y>(y: Y) => y || x)
When calling the functions using tapAsync, I receive the following error.
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))
I haven't specified any types in TypeScript on the tapped functions, but I have attempted to use generic types in the second function two without success
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))
Any assistance would be greatly appreciated!
============== edit 2020-09-01 ====================
I have been experimenting with the code and have added more detail to the types, but now I encounter an error with the two function when it returns a new object of the same shape.
new typescript playground example
const tapAsync = <X, Y>(fn: (x: X) => Promise<Y|void>) =>
(x: X): Promise<X|Y> =>
fn(x).then((y: Y|void) => y || x)