1

When I use the following code, typescript does not complain that I try to pass IAction<Product> instead of the expected IAction<Customer>:

interface IAction<T> {
    canExecute<T>(input: T): void;
}

interface Product {
    name: string,
}

interface Customer {
    displayName?: string;
    description?: string;
}

class MyAction implements IAction<Product> {
    public canExecute<Product>(product: Product): void {
    }
}

class MyMenuItemAction {
        constructor(private action: IAction<Customer>) {
    }
}

function foo(action: IAction<Customer>): void {
}

const action: IAction<Product> = new MyAction();
const menuItem = new MyMenuItemAction(action);
foo(action);

But if I remove the <T> from the canExecute function in the IAction interface, it does complain about it:

interface IAction<T> {
    canExecute(input: T): void;
}

Is this a bug in typescript?

2
  • 2
    This is not a bug in Typescript. With that extra <T> you are actually declaring a new type parameter for the method, different than the one the interface declares. Commented May 26, 2020 at 6:09
  • But I'm still confused why I can pass IAction<Product> to a function which expects IAction<Customer>...? Commented May 26, 2020 at 6:38

1 Answer 1

1

You need to change your interface to avoid override of a generic

interface IAction<T> {
    canExecute(input: T): void; // <- remove <T> from here.
    // canExecute<T> means there's another generic that has a conflict
    // with IAction<T>
}

Then update MyAction

class MyAction implements IAction<Product> {
    public canExecute(product: Product): void { // <- remove <Product>
    }
}

Profit: Playground

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

8 Comments

Thanks! can you please clarify what you mean by: "canExecute<T> means there's another generic that has a conflict with IAction<T>"
once you have <T> in definition - it means there's a generic. In your case you say IAction<T> has a generic T, then you say canExecute<T> has a generic T, but this T is an other T (name collision), and when you say IAction<Product> it doesn't set type to canExecute<T>. Therefore it's fine to have IAction<Product>.canExecute<Customer> and that's why you don't get an error.
If it answers your question - might you mark it as a correct answer?
this: IAction<Product>.canExecute<Customer> I understand. But I'm still confused why I can pass IAction<Product> to a function which expects IAction<Customer>...?
because IAction<Product> doesn't narrow the 2nd canExecute<T> and it can be any type. It means it accepts a top level Product only and canExecute accepts anything as its T and anything fits Customer therefore you don't get an error.
|

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.