Dan Lugg
Dan Lugg

Reputation: 20621

No Overload Matches; TypeScript and Generic Method

I've defined the following interface type:

interface Input<TOutput> { }

And the following extension of that interface type:

interface ExampleInput extends Input<ExampleOutput> { }

interface ExampleOutput { }

I've also defined the following "service" interface:

interface Service {
  invoke<TInput extends Input<TOutput>, TOutput>(input: TInput): Observable<TOutput>
}

However, when I try to call it with an instance of ExampleInput, it fails:

function example(service: Service) {
  service.invoke({} as ExampleInput).subscribe((output: ExampleOutput) => {
    console.log(output);
  });
}
error TS2769: No overload matches this call.
  Overload 1 of 3, '(observer?: Partial<Observer<unknown>> | undefined): Subscription', gave the following error.
    Type '(output: ExampleOutput) => void' has no properties in common with type 'Partial<Observer<unknown>>'.
  Overload 2 of 3, '(next: (value: unknown) => void): Subscription', gave the following error.
    Argument of type '(output: ExampleOutput) => void' is not assignable to parameter of type '(value: unknown) => void'.
      Types of parameters 'output' and 'value' are incompatible.
        Type 'unknown' is not assignable to type 'ExampleOutput'.
  Overload 3 of 3, '(next?: ((value: unknown) => void) | null | undefined, error?: ((error: any) => void) | null | undefined, complete?: (() => void) | null | undefined): Subscription', gave the following error.
    Argument of type '(output: ExampleOutput) => void' is not assignable to parameter of type '(value: unknown) => void'.
      Types of parameters 'output' and 'value' are incompatible.
        Type 'unknown' is not assignable to type 'ExampleOutput'.

185   service.invoke({} as ExampleInput).subscribe((output: ExampleOutput) => {
                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Why is the return type of the Observable's subscribe callback either unknown or void, when I'd expect it to be ExampleOutput?


Elaborating with a different language example, in JVM Kotlin, the following compiles and behaves as expected (with the appropriate implementaions to actually run)

fun interface Subscriber<TOutput>
{
    fun subscribe(callback: (output: TOutput) -> Unit): Unit
}

interface Input<TOutput>

interface ExampleInput : Input<ExampleOutput>

interface ExampleOutput

interface Service
{
    fun <TInput : Input<TOutput>, TOutput> invoke(input: TInput): Subscriber<TOutput>
}

fun example(service: Service)
{
    service.invoke(object : ExampleInput { }).subscribe { output: ExampleOutput ->
        println(output)
    }
}

Upvotes: 1

Views: 175

Answers (1)

Teneff
Teneff

Reputation: 32148

Currently with you definition

interface Service {
  invoke<TInput extends Input<TOutput>, TOutput>(input: TInput): Observable<TOutput>
}

it's defining a method that accepts an argument that has to have the properties of Input<TOutput> and the returned type is unknown because you haven't passed value for it. To do so you can invoke it by

service.invoke<string, number>.subscribe()

Probably you'd want to describe your service interface as with generics in the service as probably there the type is known

interface Service<O> {
  invoke(input: Input<O>): Observable<O>
}
function example(service: Service<ExampleOutput>) {
  service.invoke({}).subscribe((output) => {
    console.log(output);
  });
}

TS Playground

Upvotes: 1

Related Questions