Search code examples
typescriptpolymorphismtypescript1.8

array with interface as type (polymorphism with interfaces)


I'm trying to create an array of objects, where all objects implement the interface Foo. Here is a simplified example to demonstrate the problem:

interface Foo {
    fooAction(): any;
}

class Bar implements Foo
{
     public fooAction() {
          return "nothing important";
      }
 }

 let arrayThatINeed : Foo[] = [Bar]; // Type error: Type Bar[] is not 
                                     // assigable to type 'Foo[]'

Shouldn't this behavior be supported? If not, what are the alternatives to code such behavior?


Solution

  • You're adding the class to your array instead of the instance of that class.
    Should be:

    let arrayThatINeed : Foo[] = [new Bar()];
    

    This will also work:

    let arrayThatINeed : Foo[] = [{
        fooAction: () => { console.log("yo"); }
    }];
    

    Edit

    I'm not an angular developer so I can't relate to that, but if I understand you correctly then you need an array of classes and not instances, which in javascript means that you need an array of constructors.

    That's easy to do in typescript:

    interface FooConstructor {
        new (): Foo;
    }
    
    interface Foo {
        fooAction(): any;
    }
    
    class Bar implements Foo {
        public fooAction() {
            return "nothing important";
        }
    }
    
    let arrayThatINeed : FooConstructor[] = [Bar];
    

    You'll see that this code doesn't result in errors, but it's not correct either because that it won't complain even if you remove the implements part from the Bar class.
    I can find reasons for why that's the case, but I think that the compiler should complain about it none the less.

    You can solve that if you make Foo a class, for example:

    interface FooConstructor {
        new (): Foo;
    }
    
    abstract class Foo {
        abstract fooAction(): any;
        fn() {}
    }
    
    class Bar extends Foo {
        public fooAction() {
            return "nothing important";
        }
    }
    
    let arrayThatINeed : FooConstructor[] = [Bar];
    

    Now if you remove the extends part from Bar you'll get an error.
    But you must have at least one non-abstract method/member in Foo for it to work (that is, it will complain if what's in the array isn't a class extending Foo).