1
votes

The problem is not about the "new" keyword in the "problematic aspect", but more in the practical approach.

I'm fighting a bit with my C# and JavaScript mindsets when defining classes (ES2015). In C# I use dependency injection to inject the dependencies in the class' constructor. In JavaScript, due to its prototypical nature, I learned there's no need for you to inject a dependency into the constructor; you can safely use import, because you can later mock the method later for Unit Testing.

Therefore many modules "just work" out of the box, without the need to instantiate them. (Node modules are examples)

I've been using TypeScript in my project. As you know, you can use Interfaces with it. What I’ve been doing as of now, is setting the interface as a property and then setting the class in question to it.

To better illustrate what I'm saying, imagine I have class A:

class A implements Letter {
    hello(){
        console.log("Hello World");
    }
}

And its interface "Letter":

interface Letter {
    hello(): void
}

Then I have a class "Book" which uses the approach I'm saying.

export class Book{
    letter : Letter
    constructor(){
        this.letter = A; 
    }
    Read(){
        console.log("We're about to read this chapter!");
        this.letter.hello();
    }
}

And it can be used like this:

import {Book} from './book' 
let book = new Book();
    book.Read();

On the other hand, I have "BookVersionJs" who doesn't use any interface and implements it directly:

import {A} from './a.ts';

export class BookVersionJs{
    Read(){
        console.log("We're about to read this chapter!");
        A.Hello(); 
    }
}

Then we can use it like this:

  import {BookVersionJs} from './BookVersionJs'    
    BookVersionJs.Read(); 

But I loose the "theoretical" loose coupling that the interfaces give me. Theoretical because JavaScript could overwrite them with a prototype.

There's a difference in practicality. Which should I aim for in TypeScript/JavaScript? Or is it a matter of opinion?

1
You cannot do A.hello(), as A is a class, not an instance. You need to either do new A().hello() or make the hello method static. - Nitzan Tomer
@NitzanTomer Oooooohhhhhh I finally got it. The thing is that I was always confused because of the transpiling. This answer goes a lot on what I was trying to say. stackoverflow.com/questions/20534702/… stackoverflow.com/a/20534942/1057052 - Jose A
Yup. Problem solved. Thanks to your comment I managed to find what I was looking for ;) - Jose A

1 Answers

1
votes

It's hard to tell exactly what you're asking, but I can definitely say I like the look of your Book example far more than your BookVersionJs example (even though both have some syntax errors in there).

Think about reading books.

BookVersionJs

BookVersionJs.Read();   // "I am reading Book"

Book

let book = new Book();
book.Read();  // "I am reading a Book"

Your objects should make sense semantically or anyone else who uses your code will get very confused.

If your concern is testability, then by all means inject the Letter. You can keep A as the default type for easier usability in your day-to-day code and supply a different implementation of Letter during your unit tests.

export class Book{
    letter : Letter;
    constructor(letter: Letter = new A()) { // Note: Assign instance of A, not just A. A is a type, not a value.
        this.letter = letter; 
    }
    Read(){
        console.log("We're about to read this chapter!");
        this.letter.hello();
    }
}