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);



Pingback: Dew Drop – October 12, 2012 (#1,421) | Alvin Ashcraft's Morning Dew
Pingback: Questions About Jquery | Top Apprentice Blog