16
Dec

TypeScript; Strongly-typed in a world of prototypes

♫ Let’s get technical, technical ♫
♫ I wanna get technical ♫
Let’s get into technical ♫

– Olivia Newton-John (in my dreams)

Alright, this blog has seen too many business-related posts, it’s time to get our geek on.

Return types and type declaration? Oooh yeah.

We are mainly PHP developers at Solutions Majisti and we love strongly-typed languages. I know, I know, PHP… but it can be kind of strongly-type and we use it like so. It makes sense then that when looking at the tools to develop our front-end, we were looking for something that would also offer features a strongly-typed language does. Why pure JavaScript was not our tool of choice is a subject for a whole other post. Thankfully, TypeScript was here to save the day and boy did we like what we saw!

At first at least.

Don’t get me wrong, it’s still an awesome tool, with many features that warrant its use, but there are things we are… less than charmed with, to put it that way. Let’s go on an adventure together. An adventure Steven and me already went on, but we’re the sharing type, so hop on!

Reasons why TypeScript makes me feel like a king

Like I said, we’re not using it just for fun. We’re using it because it makes our job fun. Aside from all the little stuff like abstract classes, easier modules management, type declarations, etc there are few things that really made us jump on it for our front end development.

It’s strongly-typed

Don’t even start me on weak typing. I get the appeal, I really do, but I can’t stand using a JavaScript library and never being sure what a method will return. I don’t want to dig in code to figure out, “Oh, so it might return a string if it fails, but if it succeeds I’ll get an array”. It slows me down and I hate it. We might be biased honestly, do you know any programmer that isn’t? However, defining interfaces to design our domain and then going our separate ways, creating the actual classes and tests then merging it back in, knowing as long as the interfaces are respected, the classes will mesh without problem is too vital too us.

Look at how clean this looks:

/// <reference path="../reference.d.ts" />
import Reducer = require('../majisti/react/reducer/Reducer');
import Package = require('../majisti/react/Package');

class PackageSelectionStepReducer extends Reducer{

    private packageName:string;
    availablePackages(state:Package[], action:FluxStandardAction):Package[] {
        // Do something and return an array of Package
    }
}

export = PackageSelectionStepReducer;

I know what I need to feed the method with and I know what it will return. I don’t need to make sure my method returns the right type of object, there’s no check to make sure I got an array, the compiler enforces that I only give arrays to my availablePackages method. Even better, accessing the array, TypeScript will know they’re Package objects!

There are also interfaces, though those are a bit weird since they don’t translate to anything. JavaScript has no concept of interface, so really, there is nothing to translate them to, they are only used for TypeScript.

interface User {
    getId():number;
    getUserName():string;
}

// And then you can have someone implement it

class SuperAdmin implements User {
    getUserName():string {
        return 'Neo';
    }
}

With this, we can work with interfaces instead of expecting specific implementations! Yay! Which gives us…

Code hinting, my savior!

I mean, seriously. I wouldn’t be able to code without it. The control and space keys on my keyboard have a really difficult life. Being strongly-typed, it’s easy for my IDE to know what the current object is, what its methods are and well… everything we normally get with strong typing. How sexy is that:

code-hinting

Very. Very sexy is the answer.

Now, I can already hear some people going, “Yeah, but you have it with JavaScript too!”. No… Not really. If a method named getContainer() is implemented by three or four unrelated JavaScript objects or libraries, the IDE will list them all. After all, how can it know which one you want? It’s not even sure what the current object’s type is!

Ultimately it might be a weird requirement, but for us at Solutions Majisti code hinting is vital. It speeds up our process too much to be ignored.

let me set that variable

Have you heard of ES6? Block-scoped binding constructs, modules, classes, arrow functions, destructuring, oh my! No one supports ES6 yet though. No one except TypeScript! Well and Babel, but this guy will have a spot in my conclusion.

There’s a multitude of features that make JavaScript awesome to use, but you can’t use them in browsers yet, TypeScript lets you use them and then it’s compiler will make sure to translate it all to a language ES5-compliant browsers will understand.

// This basically means, go into this.props and find the showInformation key, return it to me and create a variable named showInformation with it.
let { showInformation } = this.props;
let { currentStep, steps } = this.props.registration;

// Will become

var showInformation = this.props.showInformation;
var _a = this.props.registration, currentStep = _a.currentStep, steps = _a.steps;

Don’t tell me this doesn’t bring a tear to your eye. It works the same with modules, classes, arrow function, etc. For a list of what can be done, look at TypeScript’s Handbook, most of it is in there. Otherwise, the roadmap is also a great place to see what’s available to you.

Generics

That's how you should feel when using generics

That’s how you should feel when using generics

Yeah, not even a joke or anything in that title. Just. Generics. They’re awesome enough on their own. Anyone who does Java know how useful they can be. Generics are a way to tell a function you don’t know what it will get, so you don’t know what it will return, but that what you get and return will have the same type.

I’m explaining this like crap, see this example:

function identity<T, U>(arg1:T, arg2:U):T {
    return arg1;
}

// What this tells TypeScript is that when calling identity(), we will receive two parameters, we don't really care what type and when we do return something, it will be the exact same type as our first parameter.

var arg1:MyObject = new MyObject();
var arg2:MyOtherObject = new MyOtherObject();
var id = identity(arg1, arg2);

// id will be of type MyObject. Not only that, code hinting will work no problem even though we have NO idea what we're getting because we're telling TypeScript our returned object will be the same type as our first parameter, which is type "T"

You could also tell you method that generic T will extend, say, a Package class and you then have access to anything a Package would expose while still having very tight code hinting since you can still give it a CanceledPackage (crap naming of that class, but ignore that) and your IDE will know the returned object will be of type CanceledPackage and not just Package. Think of the possibilities!

Generics were more of a side effect of using TypeScript, not really a selling point. But boy, oh boy, are we happy they are here.

Wow! TypeScript is the bestest!

Yes, TypeScript has some very awesome features. However, having said all that, there needs to be a second section to this post.

Reasons why TypeScript comes straight from the pits of Hell

TypeScript trying to escape

TypeScript trying to escape

Ok, ok, maybe I’m exaggerating, but there’s more than a few things we really don’t like about the language. Quirks that Solutions Majisti understands why they exist, but that really, really need to be solved for the language to really pick up. Some could even prevent you from using TypeScript at all they’re so bad and I really hate to admit it because I love the concept of TypeScript. Ultimately the language is very young too, the little guy just needs to get out of his teenage years.

Why, oh why, do typings work the way they do!?

First order of business though, what are typings? Remember that JavaScript is not typed and TypeScript is, so what happens if I want to use Mocha, Chai, AngularJS, jQuery, etc? You have two choices, ignore them and simply use them; TypeScript won’t complain, but you won’t get type hinting. Solutions Majisti loves type hinting though, so Microsoft came up with what they call typings. They are basically a file to describe a library with classes and interfaces, letting us use jQuery and know that prop() expects a string as its first parameter and that it returns an object of type JQuery.

That’s a great idea, but it is done in such a contrived and messy way, it’s unbelievable.

The worst repository in existence

A project was born out of the TypeScript community called DefinitelyTyped. It’s a collection of typings done for almost any JavaScript library you may want to use. That’s really nice, it means I don’t have to bother with figuring out how to build a definition file for jQuery, I can just download it there and start working. Awesome. Now, we’re developers and lazy so we don’t want to have to download those manually, we want a tool do to it in our stead. Good news, there is a tool for this! Introducing TSD, a package manager for TypeScript typings; just be very careful it is nowhere as smooth as npm. It does the job though.

Where it gets ugly is because it gets its typings from the DefinitelyTyped/DefinitelyTyped repository. Notice something? Everything is in the same repository! Every. Single. Typing. Who thought THAT would be a good idea!? How do you handle versioning? How do I efficiently report an issue with typing xyz? How do I contribute without having to clone that crazy repository? Answer to all those: you don’t.

That means you’re pretty much stuck with whatever is there. Sure you can contribute and maybe help with issues, but look at this issue’s title: Missing options attribute in ITemplateOptions. What typing are we talking about? No idea. Which version? Oh wait, there are no versions. Well shucks.

Ok, so let’s say I figure out that issue. The library updated and it’s API changed a bit. I have a fix. What now? I can make a pull request sure, but not all typings are tested so now I know it works for me, but don’t forget this is basically JavaScript, which means how you use a library’s method can be very flexible so it’s not impossible my fix broke someone else’s use case. Crap.

Ok, so now let’s assume I’m awesome, broke nothing, created a test for that typing and everything is perfect. Great! Wait no, now that typing is updated to the latest version of the library! What about people still on an older version? If you’re lucky (and to be fair, most of the time you are), there’s a block comment at the top of the definition file telling you which version of the library the typing was created or updated for, so you can get the commit’s id and use that in your tsd.json. That works, but I mean, really? Wouldn’t there be a better system like, oh I don’t know, versioning? Having each typing in its own repository and tagging versions would be so much better.

Little details

It’s an inconvenience and part of the pain is because I’m lazy, but it really sucks when a typing does not exist for a library you use and you have to create it yourself. This also reflects when a typing is not up to date, you now have to kind of hack away and add the missing definitions or override invalid definitions. It’s really not the worst thing ever, but it’s a thing to consider.

It requires compilation

It’s an extra step to my development. I don’t like extra steps. Of course compilation is required and it rarely goes over one second (though that requires you to be careful about your setup), but it adds up. Compilation takes a second, live reloading takes an other to realize it needs to reload and then the actual reload. Over and over again all day. That’s not counting the tests that also require to be compiled. Sometimes Karma won’t realize the tests changed, because they haven’t; Webpack is still packaging my tests. Oops. IntellijIDEA going crazy if you forget to exclude the generated files from its indexing.

It just adds to the complexity. I get why. I found solutions, but it’s still something I consider a negative. I know Atom can use a tsconfig.json file to really customize and optimize its compilation of TypeScript, but IntellijIDEA does not support it fully yet; maybe once it does I won’t have as much of a problem with this compilation issue.

Imports that last for days

import RestClient = require("../../../Ez/App/Resources/public/ts/majisti/data/RestClient");

Ew. Ewww. Ew! There is literally no concept of namespace in TypeScript, no way to configure source folders or at least use a variable in the import statement to prevent those abominations. Again, Atom has a solution for that, but that is still not something that TypeScript itself solves and that’s a problem. So you go and type ../ until finally your IDE lets you know it found the class you were looking for and you feel some relief for a little while. Until you need to import an other class.

Yes, JavaScript modules work the same way, but that’s really not a reason. TypeScript should have a way to solve that problem. Plus, I also hate how those work.

Mixin support is… Well. It’s there at least

Yeah, there are mixins, but they are so weird and contrived. You can read about them here, but the short of it is you have to declare again what your mixin is already implementing and then you have to add a random global method (and add it to your typings).

It’s cool, it’s there, at least I have mixins, but why is the method not already implemented? It could be part of TypeScript’s default typings and it could be generated automatically as soon as the compiler detects you’re using mixins. Why am I burdened with those implementation details for a “feature” of the language? As a bonus, having those things builtin would mean any time my IDE sees applyMixins() in my object, it would know to let my object inherit those mixin’s properties, which in turn means I don’t have to declare those again. Wouldn’t that be just great?

There is a way to use decorators with TypeScript which negates this problem somewhat, but it’s not fully there yet and ES7 is still very much in its infancy. I don’t know if I would want to rely on those just yet in production, but they exist and are a better way to handle composition, so in the interest of fairness, I mention them.

With all that…

Is it really required? We found solutions to what most of the problems we have with TypeScript and all in all I still find the good outweighs the bad, but I’m not convinced TypeScript is required.

Solutions Majisti adopted TypeScript a while ago, before the boom Babel had recently and looking at it, we are starting to wonder if we should jump ship. We would still have the compilation step, but at least we get rid of typings. Imports are still ugly, but at least we would have better mixin support. The grass is always greener on the other side of the fence though, so we are wary of switching. It would essentially mean all our TypeScript code is legacy now and we’d lose strong typing… Sadly, Solution Majisti does not have all the answers yet, so we really don’t know.

We have a pretty long list of pros and cons, maybe I’ll post a comparison eventually.