0

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?

  • 2
    For `plugins` you have made an array of `SceMain` *constructors*, not an array of `SceMain` *instances*. The constructors don't have, for example, `onComplete()` methods (the methods are not `static`). Perhaps you should have passed instances, e.g., `plugins: Array= [ new javajunit.CodeGenerator(), ... ]`? – jcalz Feb 13 '18 at 19:29
  • yeah I need to pass the constructors in the array, not instances, so what do I do yo –  Feb 13 '18 at 20:11
  • Then `plugins` is typed wrong. It should be something like `plugins: Array SceMain>`, assuming all the constructors are no-arg. – jcalz Feb 13 '18 at 20:12
  • ok that sounds about right...want to add an answer and collect some points? –  Feb 13 '18 at 20:12
  • seems weird to me though, instances of `CodeGenerator` are gonna have the methods defined in `SceMain`..so...in Java my original pattern would work tmk –  Feb 13 '18 at 20:14

2 Answers2

0

Because plugins is an array of constructors, not an array of instances, then the type needs to change:

// original

plugins: Array<SceMain>

// improved

plugins: Array<new() => SceMain>

thanks to @jcalz for the answer

0

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!

jcalz
  • 264,269
  • 27
  • 359
  • 360