Lazycoder

11Oct/128

First try with TypeScript

Microsoft released TypeScript (TS). This is another language that is a super-set of JavaScript and compiles to POJ (Plain Old JavaScript). It includes several features proposed in ES6 "Harmony" including optional typing and class/module support. It also includes support for interfaces.

Here is their "Hello World" example.

//POJ
function greeter(person) {
    return "Hello, " + person;
}

var user = "Jane User";

document.body.innerHTML = greeter(user);
          

//TypeScript
function greeter(person: string) {
    return "Hello, " + person;
}

var user = "Jane User";

document.body.innerHTML = greeter(user);          

The idea is that TS can compile POJ as well as TS code. I decided to give the new language a spin by converting my Bootstrap.js project over to TS. Bootstrap uses a lot of common JS idioms, but it also is missing a few features like AMD support and support for new HTML5 attributes. Plus it does have to do some type checking of optional arguments. So my hope was that TS would allow me to easily add those features and remove some of the need for type checking.

The first thing I wanted to do was created the global var that would hold my bootstrap function. It includes some private variables that are closed over and parses some optional arguments. This isn't the entire code, but it's the part I wanted to convert first since all of the other code should port over pretty easily.

var bootstrap = (function() {
    var itemsLoaded = []; //maintain a list of items we have already loaded to prevent duplicates
    var bootstrap = function() {
        var args = arguments;
        if(args.length == 0) return;

        var scriptSrc = "";
        //figure out if we were passed a script URL, a config object, or an array of script URLs
        var src = args[0];

        if(typeof src === "string") {
            scriptSrc =src;
        } else if(typeof src == "object" || typeof src == "array") {
            processConfigObject(src);
        }

        var head = document.head || document.getElementsByTagName("head")[0];
        var scriptId = args[1] || "script" + Math.floor(Math.random() * 1234);
        var loaded = false;
        var scriptTag = null;
        var callback = callback = args[2] || null;

//... lots of code here
return (window.bootstrap = window.$b = bootstrap);
}())

My first attempt looked like this:

That produced this JS code when I compiled:

That code is pretty funky. First of all, the variables which were previously closed over and private are now just in the function with the scope implied since they are function parameters.

Ok, I'm not sure exactly what I'm doing. I'm coding in one window with the TS language spec open in another window. Hardly ideal, but TS is supposed to target two kinds of developers. 1) Those that know C# but aren't familiar with JavaScript 2) Those that know JavaScript but want some type safety and easy to create structure. I'm both! (well, I don't really care too much about type checking at compile time but it would be nice if the compiler could build some of it in at runtime for me).

The first thing you notice is that all of the type checking is done at compile time. That's what we all want right? No, sometimes we also need to check types at run-time because we want to either know what variables really are as opposed to what we think they contain or because we want to do different things based on whether or not a variable contains a certain type. Consider the following code in Bootstrap, which is pretty idiomatic JavaScript code I think. It's a common pattern used to allow a variable number of option parameters to be passed to a function.

var bootstrap = function() {
        var args = arguments;
        if(args.length == 0) return;

        var scriptSrc = "";
        //figure out if we were passed a script URL, a config object, or an array of script URLs
        var src = args[0];

        if(typeof src === "string") {
            scriptSrc =src;
        } else if(typeof src == "object" || typeof src == "array") {
            processConfigObject(src);
        }

        var head = document.head || document.getElementsByTagName("head")[0];
        var scriptId = args[1] || "script" + Math.floor(Math.random() * 1234);
        var loaded = false;
        var scriptTag = null;
        var callback = callback = args[2] || null;

First, I check to make sure that at least SOME arguments were passed to the function. Then I check to see if the first argument passed is a string. If it is, the rest of the program uses that string as the source of the JavaScript file to be loaded dynamically. But, if it's an array or an object, I assume it is a configuration object. I use optional configuration objects in BootStrap to allow users to specify multiple JavaScript files to be loaded with a single call. Then I check to see if an id to use for the script tag to be inserted was passed, if not I assign a random number as the id. Lastly, I check to see if a callback was passed. I don't check to see if the callback is a function or not because I'm allowing the user to shoot their self in the foot if they want.

Note, that in TypeScript, even though the class constructor parameters are annotated with types and some are marked as optional, there is no type checking at all and I still have to manually assign the optional value using the short-circuit feature of the || operator

So that means that the only way you can get the benefit of type checking in TypeScript is to write ALL of you code in TypeScript. This means that integrating TypeScript with legacy JavaScript applications is pretty worthless.

One other annoying thing is the need to declare transient variables for stuff that isn't available until runtime. This is supposed to ensure that you call built in DOM methods or methods on other JS objects correctly. What it means is you end up with lots of compiler warnings telling you that "head" is not a property or method of "document".

Which brings me to the final annoying thing: the compiler itself.
First it processes the script file from the top down. As soon as it finds an error, it stops processing and reports the error. It does not try to parse the entire script file and report all of the errors to you. So that means you get to play whack-a-mole with the compiler. It finds an error, reports it to you, you fix it, then it finds the NEXT error. Rinse and repeat. Also, there is no distinction between compiler errors and compiler warnings. You just have to check the size of the output file. If it's 0, that was an error.

I don't see myself using TypeScript in it's current form. I know JavaScript well enough and the supposed benefits of TypeScript aren't really a benefit to someone who knows JavaScript. Looking at it from the perspective of a C# developer who doesn't really know JavaScript, it's not really C-Sharp-ey enough to help out someone who doesn't know JavaScript very well and, in the case of private and optional variables and parameters, it can actually confuse them.

One final note. The way that TypeScript was developed and released was very disappointing. They worked on it in secret without getting the JavaScript community as a whole involved from the start. Then just dumped it on us as if to say, "BEHOLD! We have solved all your problems!". Typical old school Microsoft. It's great that the spec is now open and the source has been released under an Apache license though. But really, TypeScript doesn't address any of the real problems that JavaScript has. Type checking isn't a problem in JavaScript, Type COERCION is a problem is JavaScript. Numbers are still floats in JavaScript. The DOM is still a mess without jQuery or another library. Scope is still confusing and must be managed carefully. All the TypeScript team had to do is open a copy of "JavaScript: The Good Parts", turn to the section titled "The Bad Parts" and think, "Ok, how can we fix these?"

P.S. I never did figure out how to replicate this line in TypeScript.

return (window.bootstrap = window.$b = bootstrap);
Filed under: General Leave a comment
  • http://twitter.com/colin_jack Colin Jack

    Interesting stuff but two comments.

    First is not sure I followed the bit “type checking in TypeScript is to write ALL of you code in TypeScript”, would you be able to elaborate on that a bit?

    Also on type checking, I do think type checking could have value. Declaring primitive types on arguments to methods I’m not overly worried about but I like the idea of being able to declare my own interfaces and combined with structural typing I think it could avoid some pain for me. In particular I’m hoping it helps me in being able to write self-documenting code and giving more support for refactoring (though not sure how far this will go TBH, but I’m hopeful at least).

    • http://lazycoder.com/ Scott Koon

      The only way that TypeScript will check the types being passed to a function is at compile time. Which means that both the calling code and the code being called have to be written in TypeScript. So, if you want the type checking, you have to write all of your app JS code in TypeScript. Otherwise, TypeScript just turns into an interesting metadata annotation system.
      The main goal of TypeScript is, ultimately, to give the current IDEs something to work with at design time. In my opinion, to drag JavaScript down to C#’s level to accommodate Visual Studio.

  • http://twitter.com/awright18 Adam Wright

    I think you have an interesting view of TypeScript.  I think the primary use case for the compile time checking is when you actually use it to tell it what the parameter datatypes are that you expect.  In a function where you expect any number of arguments to be passed TypeScript is definitely worthless.   The language is not designed to check types at runtime. That isn’t the point at all. It is to allow you to express the intent of how people should call the code you have written, and if they violate that intent they will be notified.   One of the other main ideas was for this to be used for very large projects with potentially a large number of files.  Again so that you don’t have to always go to a function definition to understand if you are passing the right types, it can just help you out and tell you these are the datatypes expected here.    As for writing your entire javascript in TypeScript that would make sense if you are talking about a single library, but it does not apply necessarily to using external libraries.  To take advantage of external libraries you just need to provide the interface definitions that you care about.  And if you don’t have those, then it won’t complain if you use it wrong.  I don’t think MS was trying to solve all the worlds problems with this, but just one.  To bring design time information closer to the developer when they are writing javascript code and interacting with existing code to reduce the amount of coding mistakes by making the expected datatypes clear while still allowing you the flexibility to do anything that you desire in javascript.   I do agree with one of your points.  The compiler should show all the errors that it finds instead of one at a time.  Good Luck if you decide to keep trying typescript.  I think it will mature to be a good tool for certain people working on certain types of projects, but won’t be perfect for every person, team or project.

    • http://lazycoder.com/ Scott Koon

      Hmmm, your point about using TypeScript to express your intent is an interesting one. Scott Bellware had suggested that perhaps TypeScript could become an IDL for JavaScript. In that case, I would see some use for TypeScript. There have been other attempts to bring interfaces and interface checking to JavaScript, most notably by Dustin Diaz in his book “JavaScript Design Patterns”. But ultimately the more you work with JavaScript, I believe the more you realize that you don’t really care about the type as a whole. You really only care if it accepts the message that you want to pass to the object.

      • http://about.me/mikeschinkel MikeSchinkel

        I’ve got to agree with @twitter-21332638:disqus. TypeScript is about making it easier for a library author to convey intentions of use to a library user and about enabling tooling to provide the developer help with their development.  It’s not about runtime checking nor about limiting the developer.

        Have you watched the full video[1] by Anders Hejlsberg yet?  If you’ve not I highly recommend watching the entire thing.  

        [1] http://media.ch9.ms/ch9/c3e5/e5e02f2e-5962-48db-9ddd-85e27a4fc3e5/IntroducingTSAndersH_mid.mp4

        When I first heard about TypeScript I thought “Not again” but after recognizing that Anders was involved — he wrote the language/tool that got me hooked on programming (Turbo Pascal) – I decided to give it a look. And I’m glad I did.  Using TypeScript means I wouldn’t have to figure out how to simulate in Javascript the functions that are built-in the TypeScript; it’s much easier to code with sugar than without.

        TypeScript as a language is Javascript, but better. And there’s nothing about it that requires you to use it’s features; use the ones you like/need and then use regular Javascript for the rest. if there are features you think should be implemented differently get on their forums and offer your suggestions; Andres said it is still likely to change because of ECMAScript changes; maybe from community feedback too?

        For me, I’d love to use TypeScript with Node.js but am not interested in it unless they implement a JIT compiler because I don’t want to have to add a compile step to our development process.  I’d also like to use if for WordPress plugins but again, unless I can work out a way to make it seamless then it’s probably a non-starter.  I use PhpStorm on the Mac, so unless they add support for TypeScript I don’t I’ll really be able to use it because using it will be more trouble that it’s worth.  Finally, I’d like to see a lot of traction behind it as a language so that I know there will be lots of examples available on the web before I’d commit fully to it.

        But I hope to find that all those things come to pass because I would really like to be able to code in TypeScript vs. Javascript.

      • Bob

         TypeScript will actually count any object that “accepts the message that you want to pass to the object” as a compatible “type”.

  • Pingback: Dew Drop – October 12, 2012 (#1,421) | Alvin Ashcraft's Morning Dew

  • http://twitter.com/markrendle Mark Rendle

    If they’d got the whole JavaScript/ECMAScript community involved from the start, we’d have got to this stage somewhere around the heat death of the universe.

    • http://lazycoder.com/ Scott Koon

      As it should have been.

  • Pingback: Questions About Jquery | Top Apprentice Blog