1

Is it possible to have interface in Typescript with generic methods that will allow us to tell that T type will extend some other type?

interface Abc {
  a: string;
  b: string;
  c: string;
}
interface Checkout {
  onComplete<T>(session: T): void
}
class StripeCheckout implements Checkout {
  onComplete<T extends Abc>(abc: T) {
    console.log(abc.a);
  }
}

onComplete method is invalid

Property 'onComplete' in type 'StripeCheckout' is not assignable to the same property in base type 'Checkout'.
  Type '<T extends Abc>(session: T) => void' is not assignable to type '<T>(session: T) => void'.
    Types of parameters 'session' and 'session' are incompatible.
      Type 'T' is not assignable to type 'Abc'.(2416)

Solution #1 (generic interface which will force us to tell types at the class level):

interface Abc {
  a: string;
  b: string;
  c: string;
}
interface Checkout<T> {
  onComplete(session: T): void
}
class StripeCheckout implements Checkout<Abc> {
  onComplete(session: Abc) {
    console.log(session);
  }
}

Problem is that if I will have more generic methods with different types it will look something like this Abc<T, U, V, Z>. We could fix that with multiple interfaces, however I'd like to tell Typescript about generic method type when I define method in class.

Solution #2 ("generic" with any):

interface Abc {
  a: string;
  b: string;
  c: string;
}
interface Checkout {
  onComplete(session: any): void
}
class StripeCheckout implements Checkout {
  onComplete(session: Abc) {
    console.log(session);
  }
}

Example usage:

const checkout = new StripeCheckout();
checkout.onComplete({a: '1', b: '2', c: '3'})
2
  • What is wrong with Checkout<A, B, C, D> where A,B,C,D are arguments of extended methods? Commented Nov 28, 2019 at 13:31
  • You should define constraint on interface level, otherwise you're breaking substitution principle Commented Nov 28, 2019 at 13:55

1 Answer 1

2

TypeScript, and many other languages will not allow you to do this, I would suggest the first solution.

interface Abc {
    a: string;
    b: string;
    c: string;
}
interface Default {
    a: number;
}
interface Checkout<T, A=Default> {
    onComplete(session: T): void;
    otherFunction(input: A) : void;
}
class StripeCheckout implements Checkout<Abc> {
    otherFunction(input: Default) {
        throw new Error("Method not implemented.");
    }
    onComplete(session: Abc) {
        console.log(session);
    }
}

Why? Because this way you can set limitations on your generics on the Checkout interface, giving you more control. Your interface should define the contract, not the class implementing it.

Sign up to request clarification or add additional context in comments.

Comments

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.