Power of Eloquence

Typescript vs Javascript - what's the catch?

| Comments

There’s been a lot of attention going about amongst developers working within the front-end ecosystem on how to go about making Javascript-based applications impervious to bugs on data-type checking.

If you’ve worked with Javascript for some time, you will discover a number of pain points this de-facto language comes with - not that many developers would care to admit.

For eg,

// here, num is a number
var num = 1;

// now num is string
num = '1';

If you can clearly see what’s wrong with this, then you are in the same boat with countless of developers who simply couldn’t help themselves, pulling their hair out far and wide when you’re completely stuck figuring out where did all the right (and intended) data types disappeared to. As Javascript is a dynamically weak type programming language, you’d be sure to know that landing in this awkward scenario will drive you up several walls when searching high and low for buggy behaviour in several places.

Unlike strongly typed language like Java, Ruby and Python, Javascript, in its core nature, does not have its own compiler to ensure type safety and type checking thus we have to shoulder greater responsibilities for lowering bug counts as your application scales up with greater complexity.

People from the back-end community are calling for major help to curb this madness for some time before things get out of hand.

When those voiced frustrations granted protestations loud enough, someone would sure to come along to rescue us from being ‘drowned’ in this madness.

Thus, a new innovation was born.

Typescript

A superset of types that comes with newer syntax features on top of Javascript, built by Microsoft and Google.

Features that come with proper class and interface definitions, compile-time checks and other superset tools that Javascript don’t normally come with.

Thus, many back-end developers come with wide open arms to rejoice over this. Not to mention the fact that Typescript was invented by a Delphi developer whose original goal was to overcome Javascript’s flawed features than true OOD purists couldn’t wrap their heads around.

This is very true when I first took this mini-video tutorial I found via EggHead online courses.


//declare and initialize string variable
let somestring: string = 'cool'

//declare function and its arguments with types
let somefunc = (a: string, b: string) => a + b;

// will highlight compile-time error of second argument
somefunc(somestring, 1);

When the above gets transpiled to Javascript

//declare and initialize string variable
var somestring = 'cool';
//declare function and its arguments with types
var somefunc = function (a, b) { return a + b; };
somefunc(somestring, 1);

In the first instance, I immediately felt there’s a strong presence of back-end language constructs Typescript brings. I can definitely see the major reasons why back-end OOD purists would love using Typescript much more than before. Typescript grants these people confidence that they can write back-end code within the front-end environment without worrying about all the quirks and warts Javascript terribly comes with.

Typescript brings the type-checking at compile times that Javascript so truly lacks!

The somefunc function with two supposedly string parameters comes to pre-warn developers the incorrect parameter type when making the method call, thus developers can immediately pick up the errors early on without having to touch their debug console to troubleshoot the app, from start to finish.

This is surely amazing!

Notice that, on the other side of Javascript fence, there’s no change in its code structure when type errors are found in Typescript. In fact, Javascript couldn’t care less about it.

More importantly, developers can make use of smart type detection check features Typescript offers, ensuring our codebase is ridden with buggy code before the actual (transpiled) Javascript code hit the production environment!

This is all very encouraging and exciting indeed, for a start.

However.

I wonder…

What’s the catch?

From my perspective, if you know your object-oriented principles (as you would being an OOD purist), regardless what language you pick, you’ll always have the confidence to know the ins and outs of it without having to depend on another tooling such as Typescript that will save you hours of headaches to debug. Especially for those who’s never worked with Javascript extensively as I professionally have been doing.

One particular area in mind is about how we deal with OOD concepts for Javascript.

Supposed you’re given this piece of snippet

//Typescript
//declare a class with a public method and a public variables
class Greeter {
    // public variable
    public greeting: string;
   
    constructor(message: string) {
        this.greeting = message;
    }

    //public methods here
    public greet() {
        return "Hello, " + this.greeting;
    }

    public sayGoodBye(): void {
        console.log("GoodBye" + this.greeting);
    }
}

let greeter = new Greeter("World");

greeter.greet(); // Hello World
greeter.sayGoodBye(); // Goodbye World

which transpiles to

// Javascript counterpart
var Greeter = (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        return "Hello, " + this.greeting;
    };
    Greeter.prototype.sayGoodBye = function () {
        console.log("GoodBye" + this.greeting);
    };
    return Greeter;
}());

var greeter = new Greeter("World");

greeter.greet(); // Hello World
greeter.sayGoodBye(); // Goodbye World

So this works as expected. Using Javascript’s prototypical inheritance along with self-invoking functional call patterns to emulate public methods and variables definitions is the correct way to do - design-wise.

But…

When trying to do this.

// Typescript
//declare using a private method and private variable
class Greeter {
    
    private greeting: string;

    constructor(message: string) {
        this.greeting = message;
    }

    private greet():void {
        console.log("Hello, " + this.greeting);
    }
}

let greeter = new Greeter("World");

//compile time error!
greeter.greet()

And it’s transpiled to this…

var Greeter = (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        console.log("Hello, " + this.greeting);
    };
    return Greeter;
}());
var greeter = new Greeter("World");

// oops! - This line still works!!! 
greeter.greet(); //Hello World

This is starting to look very off for me.

Why would you have a private variable greeting and a private method greet to be treated as public accessible members in Javascript? Even though Typescript forewarns you the compile-time error, and ought to discourage you to continue writing like this further, it doesn’t necessarily mean its Javascript counterpart will share the same concern. As above, it will complete its run-to-completion execution. As an OOD purist, don’t you think it would rather make more sense to practice best OOD principles for any languages you write? Isn’t it Typescript’s job to ensure Javascript environment to comply the golden rules of encapsulation instead of pretending to behave like one?

At least, the minimum I was hoping for is it should be re-written like this.


var Greeter = (function () {
    function Greeter(message) {
        //private variable
        var greeting = message;
        
        //and prviate method
        var greet = function(){
              console.log("Hello, " + greeting);
        }
    }
    
    return Greeter;
}());
var greeter = new Greeter("world");
//this will not work - greet is not accesible
greeter.greet();

Now, this truly respects proper encapsulation.

And, you want to access them via ‘public’ methods, you would then write.

// Typescript
//declare using private and public methods and variables
class Greeter {
    private greeting: string;

    constructor(message: string) {
        this.greeting = message;
    }

    private greet():void {
        console.log("Hello, " + this.greeting);
    }

    public sayHello():void {
        this.greet();
    }
}

let greeter = new Greeter("World");

greeter.sayHello();

Its Javascript equivalent

var Greeter = (function () {
    function Greeter(message) {
        //still private
        var greeting = message;
       
        // again private
        var greet = function(){
            console.log("Hello, " + greeting);
        }

        // use priviledged method to access private members of the internal 
        // object
        this.sayHello = function() {
            greet();
        }
    }
   
    return Greeter;
}());
var greeter = new Greeter("World");

//this now works.
greeter.sayHello();

Great.

But notice that we are no longer using prototypical inheritance anymore in this format. We’re using plain closures to achieve this effect, and this is the only way to achieve writing private/public methods access properly in Javascript.

For some reason, Typescript, by default, favours the prototypical properties for all object class definitions and instantiations.

Why they’d rather be doing this than the alternate approach above?…

I don’t know.

Perhaps they thought using closures in Javascript is more of a fallacy of true encapsulation than what Javascript takes credit for. Or maybe it’s one of those nuances that non-Javascript developers ie back-end people who simply abhor its internal constructs just because it’s being ridiculed for not object-oriented worthy?

Probably.

From what I read so far, it’s obvious to see Angular 2/4 is the hot favourite for back-end developers community, by default, thanks to having Typescript as its core development environment, along with their back-end frameworks that complement with this ecosystem, compared to, say, React, which is in vanilla Javascript/ES6+.

I totally get that.

It’s just a shame for them to rile up their angst-ridden thoughts against a language that comes with flaws (with no faults on its own), when the mere fact is that other professional people in the Javascript community have done their very best in devising the best approach when writing good, clean, and, arguably, OOD code.

Maybe that’s why such a 20-year old language that suffered such a bad reputation during its inception as LiveScript during post-Netscape era for so long, they thought Javascript is doomed to have no hope for redemption in the OOD software development world. Yet still, people still couldn’t ignore its juggernaut presence in today’s modern web development field, on a day by day basis.

Guess the key catch for having Typescript is to give people with much Javascript resentment some tender love for the language after all??

Who knows? ¯\_(ツ)_/¯

In spite of it, I’m still excited about Typescript developments and its future endeavour and fully support it.

Though, I would prefer they don’t just simply throw in a number of syntactic sugars like above, just to cover Javascript’s inherent weaknesses and pretend they won’t blow up on your face when you least expected.

I’m just hoping it will make Javascript better by moving three steps forward - not one step forward, two steps back.

Something for all of us to bear in mind.

Happy Coding!

PS: Some useful reading here for the curious-minded.

  1. What’s next after Typescript - https://sdtimes.com/atscript-googles-new-superset-javascript-runtime/
  2. Typescript learning resources - https://basarat.gitbooks.io/typescript/docs/why-typescript.html
  3. Javsascript OOD practices - http://www.crockford.com/javascript/private.html
  4. Try Typescript online - https://www.typescriptlang.org/play/

Comments