3
votes

One of the most common ES6-specific mistakes I've encountered is misspelled constructor method.

Obviously, this won't have any effect, because a class has default constructor method:

interface IConstructor {
    constructor: Function;
}

class Foo implements IConstructor {
  contructor() { ... }
}

How can this problem be addressed with TypeScript, preferably at compilation time?

5
"One of the most common ES6-specific mistakes I've encountered is misspelled constructor method." -- I feel the same with ReactJS. Just writing componentDldMount (see the misspell?) will fail silently, leaving you to track down the hideous bug yourself (not mentioning that you need to type the method name yourself all the time). This is why I generally dismiss APIs that provide hooks by exact method name matching. What you could do here is create a snippet in your IDE that creates the constructor method on, say, writing ctor. - John Weisz
@JohnWeisz Yes, snippets may work. But it is still may be painful if you do a prototype in Plunker. In Angular 2, hook methods are forced through interfaces, class Foo implements OnInit. I guess, a similar thing can be done with Flow. - Estus Flask
The problem with Angular 2 as well is that there is no way to force an optional hook method to be spelled right (besides distributing methods through multiple interfaces, but I'd say that's just as much prone to errors). You could say that constructor is an optional native hook method. A decorator-based approach is, IMO, much better, although decorators cannot be used with constructors, unfortunately. You cannot misspell, say, @componentDidMount, because it is detected by the TypeScript compiler. - John Weisz
This is one (and not the only one) thing they did right in c++: constructors are syntactically different there because they don't have return type. And constructor must have name that matches class name exactly. So if you misspell it, compiler will complain about invalid function declaration. For typescript, I can think of tslint rule that will require return types declared for everything except constructors. However, many people like it when compiler deduces types for them, so this rule will be less than useful.. - artem
@estus You might be interested in upvoting this feature request: github.com/Microsoft/TypeScript/issues/8476 - Asad Saeeduddin

5 Answers

2
votes

The best way to safeguard against this and save on typing is to use a snippet in your text editor to create a constructor.

For example, in my editor I just type in ctor then hit tab and it creates a default constructor for me. You'll have to look up how to do this in whatever editor your using. Most editors support snippets (if not, you should switch editors).

Writing extra code just to prevent this problem is overkill for sure.

1
votes

You can't safeguard against it, not easily.

You can work around it, for example:

class Base {
    protected constructor() {}
}

class A extends Base {
    contructor() { }
}

let a = new A(); // error: Constructor of class 'Base' is protected and only accessible within the class declaration

(code in playground)

But that's far from being ideal.

1
votes

An interface is an agreement between the type and consumers of the type. If you are trying to ensure that the class can be instantiated without any arguments by consuming code, you're already done. Whether or you declare a zero-parameter constructor, consumers of the type can instantiate it without any arguments.

If on the other hand you're trying to use the type system to ensure that a zero-parameter constructor is defined that does something, that is not really a task static type checking is well suited for. Maybe look at verifying this in your unit tests.

In other words, this:

class Foo implements IConstructor {
}

Is not meaningfully different from this:

class Foo implements IConstructor {
  constructor() {}
}

The only difference that matters:

class Foo implements IConstructor {
  constructor() {
      // ...
      /* Is this */
      // ...
  }
}

And verifying the behavior of that part is better suited to unit tests than the type checker.

0
votes

That's basically a method declaration. You can not prevent misspelled derived methods.

0
votes

To solve the problem with constructor method misspelling, I've ended up with the interface that covers all misprints I usually have that are hard to notice.

interface IConstructor {
    constructor: Function;
    constuctor?: void;
    construtor?: void;
    contructor?: void;
    cnstructor?: void;
    // ...
}

// Type '() => void' is not assignable to type 'void'.
class Foo implements IConstructor {
  contructor() {  }
}

It doesn't address the problem when constructor method is supposed to be specified but is missing.

More drastic approach is to use a subclass, as the other answer suggests.