Skip to main content

2 posts tagged with "q"

View All Tags

Upgrading to TypeScript 0.9.5 - A Personal Memoir

I recently made the step to upgrade from TypeScript 0.9.1.1 to 0.9.5. To my surprise this process was rather painful and certainly not an unalloyed pleasure. Since I'm now on the other side, so to speak, I thought I'd share my experience and cast back a rope bridge to those about to journey over the abyss.

TL;DR#

TypeScript 0.9.5 is worth making the jump to. However, if you are using Visual Studio (as I would guess many are) then you should be aware of a number of problems with the TypeScript Visual Studio tooling for TS 0.9.5. These problems can be worked around if you follow the instructions in this post.

Upgrading the Plugin#

At home I upgraded the moment TS 0.9.5 was released. This allowed me to help with migrating the Definitely Typed typings over from 0.9.1.1. And allowed me to give TS 0.9.5 a little test drive. However, I deliberately held off performing the upgrade at work until I knew that all the Definitely Typed typings had been upgraded. This was completed by the end of 2013. So in the new year it seemed a good time to make the move.

If, like me, you are using TypeScript inside Visual Studio then you'd imagine it's as simple as closing down VS, uninstalling TypeScript 0.9.1.1 from Programs and Features and then installing the new plugin. And it is if you are running IE 10 or IE 11 on your Windows machine. If you are running a lower IE version then there is a problem.

Regrettably, the TypeScript 0.9.5 plugin installer has a dependency on IE 10. Fortunately TypeScript itself has no dependency on IE 10 at all (and why would it?). This dependency appears to have been a mistake. I raised it as an issue and the TS team have said that this will be resolved in the next major release.

Happily there is a workaround if you're running IE 9 or lower which has been noted in the comments underneath the TS 0.9.5 release blog post. All you do is set the HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\svcVersion registry key value to 10.0.9200.16384 for the duration of the install.

First hurdle jumped, the upgrade continues simple enough. Then the fun starts...

Declaration Merging is dead... Sort of#

Having upgraded my plugin I opened up the project I'm working on in Visual Studio. I used NuGet to upgrade all the Definitely Typed packages to the latest (TS 0.9.5) versions. Then I tried, and failed, to compile. It was the the most obscure error I've seen in a while:

VSTSC : tsc.js(37574, 25) Microsoft JScript runtime error : Unable to get value of the property 'wrapsSomeTypeParameter': object is null or undefined

As you can see there was no indication where in my code the problem was being caused. Fortunately someone had already suffered this particular problem and logged an issue here. Digging through the comments I found a common theme; everyone experiencing the problem was using the Q typings. So what's up with that?

Strangely, if you directly referenced the Q typings everything was okay - which is how the Definitely Typed tests came to pass in the first place. But if you wanted to make use of these typings with implicit referencing (in Visual Studio since TS 0.9.1, all TypeScript files in a project are considered to be referencing each other) - well it doesn't work.

I decided to take a look at the Q typings at this point to see what was so upsetting about them. The one thing that was obvious was that these typings make use of Declaration Merging. And this made them slightly different to most of the other typing libraries that I was using. So I decided to refactor the Q typings to use the more interface driven approach the other typing libraries used in the hope that might resolve the issue.

Roughly speaking I went from:

declare function Q<T>(promise: Q.IPromise<T>): Q.Promise<T>;
declare function Q<T>(promise: JQueryPromise<T>): Q.Promise<T>;
declare function Q<T>(value: T): Q.Promise<T>;
declare module Q {
//… functions etc in here
}
declare module "q" {
export = Q;
}

To:

interface QIPromise<T> {
//… functions etc in here
}
interface QDeferred<T> {
//… functions etc in here
}
interface QPromise<T> {
//… functions etc in here
}
interface QPromiseState<T> {
//… functions etc in here
}
interface QStatic {
<t>(promise: QIPromise<T>): QPromise<T>;
<t>(promise: JQueryPromise<T>): QPromise<T>;
<t>(value: T): QPromise<T>;
//… other functions etc continue here
}
declare module "q" {
export = Q;
}
declare var Q: QStatic;
</t></t></t>

And that fixed the obscure 'wrapsSomeTypeParameter' error. The full source code of these amended typings can be found as a GitHub Repo here in case you want to use it yourself. (I did originally consider adding this to Definitely Typed but opted not to in the end - see discussion on GitHub.)

The Promised Land#

You're there. You've upgraded to the new plugin and the new typings. All is compiling as it should and the language service is working as well. Was it worth it? I think yes, for the following reasons:

  1. TS 0.9.5 compiles faster, and hogs less memory.
  2. When we compiled with TS 0.9.5 we found there were a couple of bugs in our codebase which the tightened up compiler was now detecting. Essentially where we'd assumed types were flowing through to functions there were a couple of occasions with TS 0.9.1.1 where they weren't. Where we'd assumed we had a type of T available in a function whereas it was actually a type of any. I was really surprised that this was the case since we were already making use of noImplicitAny compiler flag in our project. So where a type had changed and a retired property was being referenced TS 0.9.5 picked up an error that TS 0.9.1.1 had not. Good catch!
  3. And finally (and I know these are really minor), the compiled JS is a little different now. Firstly, the compiled JS features all of TypeScript comments in the positions that you might hope for. Previously it seemed that about 75% came along for the ride and ended up in some strange locations sometimes. Secondly, enums are treated differently during compilation now - where it makes sense the actual backing value of an enum is used rather than going through the JavaScript construct. So it's a bit like a const I guess - presumably this allows JavaScript engines to optimise a little more.

I hope I haven't put you off with this post. I think TypeScript 0.9.5 is well worth making the leap for - and hopefully by reading this you'll have saved yourself from a few of the rough edges.

Rolling your own confirm mechanism using Promises and jQuery UI

It is said that a picture speaks a thousand words. So here's two:

That's right, we're here to talk about the confirm dialog. Or, more specifically, how we can make our own confirm dialog.

JavaScript in the browser has had the window.confirm method for the longest time. This method takes a string as an argument and displays it in the form of a dialog, giving the user the option to click on either an "OK" or a "Cancel" button. If the user clicks "OK" the method returns true, if the user clicks "Cancel" the method returns false.

window.confirm is wonderful in one way - it has a simple API which is easy to grok. But regardless of the browser, window.confirm is always as ugly as sin. Look at the first picture in this blog post; hideous. Or, put more dispassionately, it's not terribly configurable; want to change the button text? You can't. Want to change the styling of the dialog? You can't. You get the picture.

Making confirm 2.0#

jQuery UI's dialog has been around for a long time. I've been using it for a long time. But, if you look at the API, you'll see it works in a very different way to window.confirm - basically it's all about the callbacks. My intention was to create a mechanism which allowed me to prompt the user with jQuery UI's tried and tested dialog, but to expose it in a way that embraced the simplicity of the window.confirm API.

How to do this? Promises! To quote Martin Fowler (makes you look smart when you do that):

"In Javascript, promises are objects which represent the pending result of an asynchronous operation. You can use these to schedule further activity after the asynchronous operation has completed by supplying a callback."

When we show our dialog we are in asynchronous land; waiting for the user to click "OK" or "Cancel". When they do, we need to act on their response. So if our custom confirm dialog returns a promise of a boolean (true when the users click "OK", false otherwise) then that should be exactly what we need. I'm going to use Q for promises. (Nothing particularly special about Q - it's one of many Promises / A+ compliant implementations available.)

Here's my custom confirm dialog:

/**
* Show a "confirm" dialog to the user (using jQuery UI's dialog)
*
* @param {string} message The message to display to the user
* @param {string} okButtonText OPTIONAL - The OK button text, defaults to "Yes"
* @param {string} cancelButtonText OPTIONAL - The Cancel button text, defaults to "No"
* @param {string} title OPTIONAL - The title of the dialog box, defaults to "Confirm..."
* @returns {Q.Promise<boolean>} A promise of a boolean value
*/
function confirmDialog(message, okButtonText, cancelButtonText, title) {
okButtonText = okButtonText || "Yes";
cancelButtonText = cancelButtonText || "No";
title = title || "Confirm...";
var deferred = Q.defer();
$('<div title="' + title + '">' + message + '</div>').dialog({
modal: true,
buttons: [{
// The OK button
text: okButtonText,
click: function () {
// Resolve the promise as true indicating the user clicked "OK"
deferred.resolve(true);
$(this).dialog("close");
}
}, {
// The Cancel button
text: cancelButtonText,
click: function () {
$(this).dialog("close");
}
}],
close: function (event, ui) {
// Destroy the jQuery UI dialog and remove it from the DOM
$(this).dialog("destroy").remove();
// If the promise has not yet been resolved (eg the user clicked the close icon)
// then resolve the promise as false indicating the user did *not* click "OK"
if (deferred.promise.isPending()) {
deferred.resolve(false);
}
}
});
return deferred.promise;
}

What's happening here? Well first of all, if okButtonText, cancelButtonText or title have false-y values then they are initialised to defaults. Next, we create a deferred object with Q. Then we create our modal dialog using jQuery UI. There's a few things worth noting about this:

  • We're not dependent on the dialog markup being in our HTML from the off. We create a brand new element which gets added to the DOM when the dialog is created. (I draw attention to this as the jQuery UI dialog documentation doesn't mention that you can use this approach - and frankly I prefer it.)
  • The "OK" and "Cancel" buttons are initialised with the string values stored in okButtonText and cancelButtonText. So by default, "Yes" and "No".
  • If the user clicks the "OK" button then the promise is resolved with a value of true.
  • If the dialog closes and the promise has not been resolved then the promise is resolved with a value of false. This covers people clicking on the "Cancel" button as well as closing the dialog through other means.

Finally we return the promise from our deferred object.

Going from window.confirm to confirmDialog#

It's very simple to move from using window.confirm to confirmDialog. Take this example:

if (window.confirm("Are you sure?")) {
// Do something
}

Becomes:

confirmDialog("Are you sure?").then(function(confirmed) {
if (confirmed) {
// Do something
}
});

There's no more to it than that.

And finally a demo...#

With the JSFiddle below you can create your own custom dialogs and see the result of clicking on either the "OK" or "Cancel" buttons.