Search code examples
javascriptnode.jstypescripttypescript2.0tsc

Abstract interface cannot be used as type in array - missing property


I have this interface:

export interface SceEvent {
  // ...
}

export interface SceMain {

  onComplete: () => void;

  onNextEvent: (ev: SceEvent) => void;

  getRawGeneratedCode: () => string;

  getStyledGeneratedCode: () => string;

}

I implement this interface in several files that look like this:

import {SceEvent, SceMain} from "../../interfaces";

export class CodeGenerator implements SceMain {

  private rawCode = '';
  private styledCode = '';
  public static pluginName = 'java-junit';

  constructor(){

  }

  getRawGeneratedCode() {
    return this.rawCode;
  }


  getStyledGeneratedCode() {
    return this.styledCode;
  }

  onComplete(){

  }


  onNextEvent(ev: SceEvent) {

  }

}

then in another file, I export all of these implementations:

import {SceMain} from "./interfaces";

// generators
import * as javajunit from './values/java-junit';
import * as javatestng from './values/java-test-ng';
import * as nodejsmocha from './values/nodejs-mocha';
import * as nodejssuman from './values/nodejs-suman';


export const plugins : Array<SceMain>= [

  javajunit.CodeGenerator,
  javatestng.CodeGenerator,
  nodejsmocha.CodeGenerator,
  nodejssuman.CodeGenerator

];

but I am getting this error:

Property "onComplete" is missing in typeof CodeGenerator

I don't get it, because all of my CodeGenerator classes implement this method.

Here is the error at an image...

enter image description here

Does anyone know what might be going on here?


Solution

  • The plugins constant is an array of SceMain constructors, not an array of SceMain instances. An instance of SceMain has methods like onComplete(), but a constructor does not (unless it happens to have static methods with the right signatures). The compiler is warning you that the elements of the array are not instances of SceMain.

    Assuming you actually intend to create an array of constructors (as you mentioned in your comment), you should type plugins like:

    export const plugins : Array<new() => SceMain>= [    
      javajunit.CodeGenerator,
      javatestng.CodeGenerator,
      nodejsmocha.CodeGenerator,
      nodejssuman.CodeGenerator    
    ];
    

    The type new() => SceMain is the type of a no-argument constructor which produces an SceMain instance. (If your constructors take arguments then the type should be modified.)


    Your confusion quite possibly stems from the fact that in TypeScript, when you declare a named class like class Foo {}, it creates a type named Foo, corresponding to instances of the class, and a value named Foo, which is the constructor of the class. The value Foo is not of type Foo. It is, however, of type new ()=>Foo. You can get a more specific type of a class constructor (including static methods) by using the typeof type operator: typeof Foo is the type of the Foo constructor. You can read about this more in the TypeScript Handbook.

    The distinction between type expressions and value expressions is tricky to explain, especially since TypeScript tends to use the same keywords in both kinds of expression to mean different (and possibly only marginally related) things:

    class Foo {}
    
    const foo: Foo = new Foo();  
    // the first Foo is the instance type, 
    // the second Foo is the constructor value
    
    const fooConstructor: typeof Foo = Foo; 
    // the first Foo is the constructor value
    // typeof is a type query acting on it at design time
    // the second Foo is the constructor value
    
    const theWordFunction: string = typeof Foo;
    // Foo is the constructor value
    // typeof is the JavaScript typeof operator acting on it at runtime
    // It will just be "function"
    

    I don't know if that cleared things up or made it worse. Anyway, good luck!