Skip to content

Unable to infer types from callback in bindCallback function for RxJS #22952

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
benlesh opened this issue Mar 28, 2018 · 5 comments
Closed

Unable to infer types from callback in bindCallback function for RxJS #22952

benlesh opened this issue Mar 28, 2018 · 5 comments
Labels
Needs More Info The issue still hasn't been fully clarified

Comments

@benlesh
Copy link

benlesh commented Mar 28, 2018

Currently type inference is unable to provide useful type inference for functions such as RxJS's bindCallback.

I have a PR here (ReactiveX/rxjs#3480) that tries to add overloads to support the desired behavior, but when I test it, it does not infer types properly.

TypeScript Version: @latest as of this moment

Search Terms: callback type inference, wrapping node callbacks, variadic arguments

Code

export function bindCallback(callbackFunc: (callback: () => any) => any, scheduler?: SchedulerLike): () => Observable<void>;
export function bindCallback<R1>(callbackFunc: (callback: (res1: R1) => any) => any, scheduler?: SchedulerLike): () => Observable<R1>;
export function bindCallback<R1, R2>(callbackFunc: (callback: (res1: R1, res2: R2) => any) => any, scheduler?: SchedulerLike): () => Observable<[R1, R2]>;
export function bindCallback<R1, R2, R3>(callbackFunc: (callback: (res1: R1, res2: R2, res3: R3) => any) => any, scheduler?: SchedulerLike): () => Observable<[R1, R2, R3]>;
export function bindCallback<A1>(callbackFunc: (arg1: A1, callback: () => any) => any, scheduler?: SchedulerLike): (arg1: A1) => Observable<void>;
export function bindCallback<A1, R1>(callbackFunc: (arg1: A1, callback: (res1: R1) => any) => any, scheduler?: SchedulerLike): (arg1: A1) => Observable<R1>;
export function bindCallback<A1, R1, R2>(callbackFunc: (arg1: A1, callback: (res1: R1, res2: R2) => any) => any, scheduler?: SchedulerLike): (arg1: A1) => Observable<[R1, R2]>;
export function bindCallback<A1, R1, R2, R3>(callbackFunc: (arg1: A1, callback: (res1: R1, res2: R2, res3: R3) => any) => any, scheduler?: SchedulerLike): (arg1: A1) => Observable<[R1, R2, R3]>;
export function bindCallback<A1, A2>(callbackFunc: (arg1: A1, arg2: A2, callback: () => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2) => Observable<void>;
export function bindCallback<A1, A2, R1>(callbackFunc: (arg1: A1, arg2: A2, callback: (res1: R1) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2) => Observable<R1>;
export function bindCallback<A1, A2, R1, R2>(callbackFunc: (arg1: A1, arg2: A2, callback: (res1: R1, res2: R2) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2) => Observable<[R1, R2]>;

function foo(a: number, b: number, callback: (c: string, d: string) => any): any {
   setTimeout(() => callback(a + '!', b + '!');
}

const source = bindCallback(foo)(1, 3);

Expected behavior:

source should be Observable<[string, string]>

Actual behavior:

source is Observable<void>

Playground Link:
https://www.typescriptlang.org/play/#src=interface%20SchedulerLike%20%7B%20%7D%3B%0D%0A%0D%0Ainterface%20Observable%3CT%3E%20%7B%20%7D%3B%0D%0A%0D%0Aexport%20function%20bindCallback(callbackFunc%3A%20(callback%3A%20()%20%3D%3E%20any)%20%3D%3E%20any%2C%20scheduler%3F%3A%20SchedulerLike)%3A%20()%20%3D%3E%20Observable%3Cvoid%3E%3B%0D%0Aexport%20function%20bindCallback%3CR1%3E(callbackFunc%3A%20(callback%3A%20(res1%3A%20R1)%20%3D%3E%20any)%20%3D%3E%20any%2C%20scheduler%3F%3A%20SchedulerLike)%3A%20()%20%3D%3E%20Observable%3CR1%3E%3B%0D%0Aexport%20function%20bindCallback%3CR1%2C%20R2%3E(callbackFunc%3A%20(callback%3A%20(res1%3A%20R1%2C%20res2%3A%20R2)%20%3D%3E%20any)%20%3D%3E%20any%2C%20scheduler%3F%3A%20SchedulerLike)%3A%20()%20%3D%3E%20Observable%3C%5BR1%2C%20R2%5D%3E%3B%0D%0Aexport%20function%20bindCallback%3CR1%2C%20R2%2C%20R3%3E(callbackFunc%3A%20(callback%3A%20(res1%3A%20R1%2C%20res2%3A%20R2%2C%20res3%3A%20R3)%20%3D%3E%20any)%20%3D%3E%20any%2C%20scheduler%3F%3A%20SchedulerLike)%3A%20()%20%3D%3E%20Observable%3C%5BR1%2C%20R2%2C%20R3%5D%3E%3B%0D%0Aexport%20function%20bindCallback%3CA1%3E(callbackFunc%3A%20(arg1%3A%20A1%2C%20callback%3A%20()%20%3D%3E%20any)%20%3D%3E%20any%2C%20scheduler%3F%3A%20SchedulerLike)%3A%20(arg1%3A%20A1)%20%3D%3E%20Observable%3Cvoid%3E%3B%0D%0Aexport%20function%20bindCallback%3CA1%2C%20R1%3E(callbackFunc%3A%20(arg1%3A%20A1%2C%20callback%3A%20(res1%3A%20R1)%20%3D%3E%20any)%20%3D%3E%20any%2C%20scheduler%3F%3A%20SchedulerLike)%3A%20(arg1%3A%20A1)%20%3D%3E%20Observable%3CR1%3E%3B%0D%0Aexport%20function%20bindCallback%3CA1%2C%20R1%2C%20R2%3E(callbackFunc%3A%20(arg1%3A%20A1%2C%20callback%3A%20(res1%3A%20R1%2C%20res2%3A%20R2)%20%3D%3E%20any)%20%3D%3E%20any%2C%20scheduler%3F%3A%20SchedulerLike)%3A%20(arg1%3A%20A1)%20%3D%3E%20Observable%3C%5BR1%2C%20R2%5D%3E%3B%0D%0Aexport%20function%20bindCallback%3CA1%2C%20R1%2C%20R2%2C%20R3%3E(callbackFunc%3A%20(arg1%3A%20A1%2C%20callback%3A%20(res1%3A%20R1%2C%20res2%3A%20R2%2C%20res3%3A%20R3)%20%3D%3E%20any)%20%3D%3E%20any%2C%20scheduler%3F%3A%20SchedulerLike)%3A%20(arg1%3A%20A1)%20%3D%3E%20Observable%3C%5BR1%2C%20R2%2C%20R3%5D%3E%3B%0D%0Aexport%20function%20bindCallback%3CA1%2C%20A2%3E(callbackFunc%3A%20(arg1%3A%20A1%2C%20arg2%3A%20A2%2C%20callback%3A%20()%20%3D%3E%20any)%20%3D%3E%20any%2C%20scheduler%3F%3A%20SchedulerLike)%3A%20(arg1%3A%20A1%2C%20arg2%3A%20A2)%20%3D%3E%20Observable%3Cvoid%3E%3B%0D%0Aexport%20function%20bindCallback%3CA1%2C%20A2%2C%20R1%3E(callbackFunc%3A%20(arg1%3A%20A1%2C%20arg2%3A%20A2%2C%20callback%3A%20(res1%3A%20R1)%20%3D%3E%20any)%20%3D%3E%20any%2C%20scheduler%3F%3A%20SchedulerLike)%3A%20(arg1%3A%20A1%2C%20arg2%3A%20A2)%20%3D%3E%20Observable%3CR1%3E%3B%0D%0Aexport%20function%20bindCallback%3CA1%2C%20A2%2C%20R1%2C%20R2%3E(callbackFunc%3A%20(arg1%3A%20A1%2C%20arg2%3A%20A2%2C%20callback%3A%20(res1%3A%20R1%2C%20res2%3A%20R2)%20%3D%3E%20any)%20%3D%3E%20any%2C%20scheduler%3F%3A%20SchedulerLike)%3A%20(arg1%3A%20A1%2C%20arg2%3A%20A2)%20%3D%3E%20Observable%3C%5BR1%2C%20R2%5D%3E%3B%0D%0Aexport%20function%20bindCallback%3CT%3E(callbackFunc%3A%20Function%2C%0D%0A%20%20scheduler%3F%3A%20SchedulerLike)%3A%20(...args%3A%20any%5B%5D)%20%3D%3E%20Observable%3CT%3E%20%7B%0D%0A%20%20return%20(...args%3A%20any%5B%5D)%20%3D%3E%20%7B%0D%0A%20%20%20%20return%20null%20as%20Observable%3Cany%3E%3B%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Afunction%20foo(a%3A%20number%2C%20b%3A%20number%2C%20callback%3A%20(c%3A%20string%2C%20d%3A%20string)%20%3D%3E%20any)%3A%20any%20%7B%0D%0A%20%20%20setTimeout(()%20%3D%3E%20callback(a%20%2B%20'!'%2C%20b%20%2B%20'!')%3B%0D%0A%7D%0D%0A%0D%0Aconst%20source%20%3D%20bindCallback(foo)(1%2C%203)%3B

Related Issues:

@mhegazy
Copy link
Contributor

mhegazy commented Mar 28, 2018

From a cursory look, seems like you just ordered your overloads in the opposite order. i am guessing you want export function bindCallback<A1, A2, R1, R2> ... to be at the top, since bindCallback<A1, A2>(callbackFunc: ... that you have more generic and overload resolution proceeds in order of declarations.

@mhegazy mhegazy added the Needs More Info The issue still hasn't been fully clarified label Mar 28, 2018
@benlesh
Copy link
Author

benlesh commented Mar 29, 2018

Thank you, @mhegazy, that worked, however it's REALLY ugly. Haha. Ideally, there would be some way to have "spread generics" or the like.

Here's what the type overloads for RxJS's bindCallback looks like now:

export function bindCallback<R1, R2, R3, R4>(callbackFunc: (callback: (res1: R1, res2: R2, res3: R3, res4: R4, ...args: any[]) => any) => any, scheduler?: SchedulerLike): () => Observable<any[]>;
export function bindCallback<R1, R2, R3>(callbackFunc: (callback: (res1: R1, res2: R2, res3: R3) => any) => any, scheduler?: SchedulerLike): () => Observable<[R1, R2, R3]>;
export function bindCallback<R1, R2>(callbackFunc: (callback: (res1: R1, res2: R2) => any) => any, scheduler?: SchedulerLike): () => Observable<[R1, R2]>;
export function bindCallback<R1>(callbackFunc: (callback: (res1: R1) => any) => any, scheduler?: SchedulerLike): () => Observable<R1>;
export function bindCallback(callbackFunc: (callback: () => any) => any, scheduler?: SchedulerLike): () => Observable<void>;

export function bindCallback<A1, R1, R2, R3, R4>(callbackFunc: (arg1: A1, callback: (res1: R1, res2: R2, res3: R3, res4: R4, ...args: any[]) => any) => any, scheduler?: SchedulerLike): (arg1: A1) => Observable<any[]>;
export function bindCallback<A1, R1, R2, R3>(callbackFunc: (arg1: A1, callback: (res1: R1, res2: R2, res3: R3) => any) => any, scheduler?: SchedulerLike): (arg1: A1) => Observable<[R1, R2, R3]>;
export function bindCallback<A1, R1, R2>(callbackFunc: (arg1: A1, callback: (res1: R1, res2: R2) => any) => any, scheduler?: SchedulerLike): (arg1: A1) => Observable<[R1, R2]>;
export function bindCallback<A1, R1>(callbackFunc: (arg1: A1, callback: (res1: R1) => any) => any, scheduler?: SchedulerLike): (arg1: A1) => Observable<R1>;
export function bindCallback<A1>(callbackFunc: (arg1: A1, callback: () => any) => any, scheduler?: SchedulerLike): (arg1: A1) => Observable<void>;

export function bindCallback<A1, A2, R1, R2, R3, R4>(callbackFunc: (arg1: A1, arg2: A2, callback: (res1: R1, res2: R2, res3: R3, res4: R4, ...args: any[]) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2) => Observable<any[]>;
export function bindCallback<A1, A2, R1, R2, R3>(callbackFunc: (arg1: A1, arg2: A2, callback: (res1: R1, res2: R2, res3: R3) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2) => Observable<[R1, R2, R3]>;
export function bindCallback<A1, A2, R1, R2>(callbackFunc: (arg1: A1, arg2: A2, callback: (res1: R1, res2: R2) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2) => Observable<[R1, R2]>;
export function bindCallback<A1, A2, R1>(callbackFunc: (arg1: A1, arg2: A2, callback: (res1: R1) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2) => Observable<R1>;
export function bindCallback<A1, A2>(callbackFunc: (arg1: A1, arg2: A2, callback: () => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2) => Observable<void>;

export function bindCallback<A1, A2, A3, R1, R2, R3, R4>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, callback: (res1: R1, res2: R2, res3: R3, res4: R4, ...args: any[]) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3) => Observable<any[]>;
export function bindCallback<A1, A2, A3, R1, R2, R3>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, callback: (res1: R1, res2: R2, res3: R3) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3) => Observable<[R1, R2, R3]>;
export function bindCallback<A1, A2, A3, R1, R2>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, callback: (res1: R1, res2: R2) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3) => Observable<[R1, R2]>;
export function bindCallback<A1, A2, A3, R1>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, callback: (res1: R1) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3) => Observable<R1>;
export function bindCallback<A1, A2, A3>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, callback: () => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3) => Observable<void>;

export function bindCallback<A1, A2, A3, A4, R1, R2, R3, R4>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, callback: (res1: R1, res2: R2, res3: R3, res4: R4, ...args: any[]) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3, arg4: A4) => Observable<any[]>;
export function bindCallback<A1, A2, A3, A4, R1, R2, R3>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, callback: (res1: R1, res2: R2, res3: R3) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3, arg4: A4) => Observable<[R1, R2, R3]>;
export function bindCallback<A1, A2, A3, A4, R1, R2>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, callback: (res1: R1, res2: R2) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3, arg4: A4) => Observable<[R1, R2]>;
export function bindCallback<A1, A2, A3, A4, R1>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, callback: (res1: R1) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3, arg4: A4) => Observable<R1>;
export function bindCallback<A1, A2, A3, A4>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, callback: () => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3, arg4: A4) => Observable<void>;

export function bindCallback<A1, A2, A3, A4, A5, R1, R2, R3, R4>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, callback: (res1: R1, res2: R2, res3: R3, res4: R4, ...args: any[]) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) => Observable<any[]>;
export function bindCallback<A1, A2, A3, A4, A5, R1, R2, R3>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, callback: (res1: R1, res2: R2, res3: R3) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) => Observable<[R1, R2, R3]>;
export function bindCallback<A1, A2, A3, A4, A5, R1, R2>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, callback: (res1: R1, res2: R2) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) => Observable<[R1, R2]>;
export function bindCallback<A1, A2, A3, A4, A5, R1>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, callback: (res1: R1) => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) => Observable<R1>;
export function bindCallback<A1, A2, A3, A4, A5>(callbackFunc: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, callback: () => any) => any, scheduler?: SchedulerLike): (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) => Observable<void>;

export function bindCallback<A, R>(callbackFunc: (...args: Array<A | ((result: R) => any)>) => any, scheduler?: SchedulerLike): (...args: A[]) => Observable<R>;
export function bindCallback<A, R>(callbackFunc: (...args: Array<A | ((...results: R[]) => any)>) => any, scheduler?: SchedulerLike): (...args: A[]) => Observable<R[]>;

export function bindCallback(callbackFunc: Function, scheduler?: SchedulerLike): (...args: any[]) => Observable<any>;

It doesn't support every use case, it just seemed silly to go beyond a certain point.

@benlesh
Copy link
Author

benlesh commented Mar 29, 2018

In particular, notice above the lines that have an R4, just so it can ignore that type and return Observable<any[]>

@mhegazy
Copy link
Contributor

mhegazy commented Mar 29, 2018

Yeah, you need variadic types. We have been talking about it a lot lately, and i think we will start tackling it soon.

@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@microsoft microsoft locked and limited conversation to collaborators Jul 25, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Needs More Info The issue still hasn't been fully clarified
Projects
None yet
Development

No branches or pull requests

3 participants