2

Consider the following test case :

interface BaseFoo {}

interface FooAdapter {
  method<F extends BaseFoo>(foo:F):string;
}


interface ConcreteFoo extends BaseFoo {
  value:string;
}

class ConcreteFooAdapter implements FooAdapter {
  method(foo: ConcreteFoo): string {
    return foo.value;
  }
}

The only error is with the method signature, where TypeScript complaints that :

Property 'value' is missing in type 'BaseFoo' but required in type 'ConcreteFoo'.

Why would value be present in BaseFoo since the generic F is supposed to extend it?

But, more importantly, how to fix this so there is no error?

Edit

Here is an alternative solution I was trying to investigate, but with similar failure:

interface BarAdapter {
  method<F>(bar:F):string;
}

type Bar = {
  value:string;
}

class ConcreteBarAdapter implements BarAdapter {
  method(bar:Bar):string {
    return bar.value;
  }
}

It complaints that F is not assignable to type Bar, and I don't understand why.

6
  • I'd say you've misplaced the generic type parameter scope. You probably mean to have FooAdapter be a generic interface instead of having FooAdapter have a generic method. Like this playground link shows. With your version, callers choose the type argument F. Does that address your question fully? If so I could write up an answer explaining; if not, what am I missing? Commented Dec 4, 2022 at 4:53
  • Your updated edit has the same problem; a BarAdapter's method must work for any F the caller chooses, so a caller should be able to write barAdapter.method(123). But ConcreteBarAdapter's method only accepts Bars, so it's not a valid BarAdapter. Again, I think you want to make BarAdapter a generic interface with a specific method, instead of a specific interface with a generic method, as shown in this playground link. Does that work for you or not? If it works I will write up an answer (probably tomorrow because it's my bedtime now 😴) Commented Dec 4, 2022 at 5:15
  • I was hoping to have the generics at the method level, but it does work, yes. Commented Dec 4, 2022 at 5:21
  • Can you explain why you wanted the generics there? Is there a use case for that? Commented Dec 4, 2022 at 5:22
  • 1
    Oh well, I see that @GuerricP has posted essentially this answer, so I won't bother with mine. Commented Dec 4, 2022 at 14:11

1 Answer 1

1

If your only criteria is that the parameter should extend BaseFoo, and the return value should be a string, you may not need generics at all, this would be enough:

interface BaseFoo { }

interface FooAdapter {
  method(foo: BaseFoo): string;
}

interface ConcreteFoo extends BaseFoo {
  value: string;
}

class ConcreteFooAdapter implements FooAdapter {
  method(foo: ConcreteFoo): string {
    return foo.value;
  }
}

This provide as strong typing as in your attempt with generics. TypeScript constraints method in the implementor to extend method(foo: BaseFoo): string.

However, if you need to be able to use the adapters as implementors of a specific method signature, then you'd have to add the generic parameter on the interface, and then provide the type explicitly when implementing it:

interface BaseFoo { }

interface FooAdapter<F extends BaseFoo>  {
  method(foo: F): string;
}


interface ConcreteFoo extends BaseFoo {
  value: string;
}

class ConcreteFooAdapter implements FooAdapter<ConcreteFoo> {
  method(foo: ConcreteFoo): string {
    return foo.value;
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Ah! Yes, even if generics are fancy and really useful, sometimes the simple solution is best. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.