Tuesday, November 27, 2012

Various TypeScript Weirdnesses

I’ve been playing around quite a bit with TypeScript lately. I’m no fan of JavaScript – Douglas Crockford is crocked, IMO – so the idea that it might be possible to fix its enormous problems while retaining its real strengths has a whole lot of innate appeal to me. In addition, the fact that Anders Hejlsberg (the genius behind C#) is also behind TypeScript gives the project some immediately credibility.

I also saw an immediate use for it. I’m currently in the process of rewriting Alanta’s API, and during the first two iterations of the API, I struggled no end with getting JavaScript to act like a reasonable, modern language. It’s sort of possible, but given JavaScript’s extremely extensible nature, the potential for the tools to help you the way that they help you in C# or Java (or even C++) is pretty limited. You can force JavaScript into supporting things like inheritance and polymorphism, but it’s not pretty, and it’s easy to make mistakes.

So when it came time to start work on V3 of Alanta’s API, I decided to take the dive, and rewrite it all in TypeScript. I’m a couple thousand lines into it so far, and my initial conclusions have four parts:

(1) TypeScript and the tools around it are still pretty buggy.

Here’s an example. With VS2012 and version 0.8.1 of the TS compiler and tools, try typing this into a TS file:

class A {}
class A extends A {}

It’s nonsense code of course, but it’ll also hang VS2012+TS 0.8.1 hard.

(2) TypeScript still doesn’t have many of the things you’d expect from a modern language.

Generics are the big one here. You can get strongly typed arrays, which is kinda helpful:

class Animal { }
class Dog extends Animal { }
class Plant { }
var animals: Animal[] = [];

// These work
animals.push(new Animal());
animals.push(new Dog());

// This won’t compile
animals.push(new Plant());

But you can’t (yet) do something like this:

class ViewModelBase<TModel> {

    private model: TModel;
    private callbacks: { (model: TModel): void; }[] = [];

    constructor (model: TModel) {
        this.setModel(model);
    }

    setModel(model: TModel): void {
        this.model = model;
        this.raiseNewModel();
    }

    onNewModel(callback: (model: TModel) => void ) {
        this.callbacks.push(callback);
    }

    private raiseNewModel() {
        for (var i = 0; i < this.callbacks.length; i++) {
            this.callbacks[i](this.model);
        }
    }
}

Nor is there support for “async/await”, like the latest release of C#, nor any support for XML Doc or jsDoc comments, or extension methods, or protected methods, or even conditional compilation. And lots and lots of others. But I suspect that most of those will come in time; and of course, with a few caveats, JavaScript doesn’t have support for any of these either, right?

(3) TypeScript has some really weird, unexpected behaviors

This is probably my biggest issue with the language so far. Some of these behaviors are presumably “as-designed”, some might be actual bugs, and it’s not unreasonable to expect that many of them will change before the language is officially released. But they still represent some significant “gotchas” when you’re first getting used to the language as it stands right now.

One example is the weird behavior of the “this” keyword. If you’ve done any web coding at all, you know that “this” in JavaScript refers not to the class in which the method is defined, but to the object to which the method in question has been assigned. Given that JavaScript doesn’t have native support for classes, this sort of vaguely makes sense, but TypeScript is just different enough that you’re likely to get confused all over again.

For instance, take a look at this bit of code below.

class Foo {
    constructor () {
        document.onmousemove = this.showMessage;
    }
    message: string = "Hello";
    showMessage(e?: MouseEvent) {
        console.log(this.message);
    }
}

var foo = new Foo();
foo.showMessage();

Calling “foo.showMessage()” works as you’d expect. But when exactly the same method is called from the “document.onmousemove” handler, “this” gets assigned to the global “document” variable, and as a result “this.message” is undefined. That’s pretty close to how JavaScript acts, but not how “this” behaves in any other class-based language I know of. You wouldn’t normally expect a class method to exhibit entirely different behavior, depending on how it’s called. The workaround for it, by the way, is a tad odd, if handy: just assign the event handler like this:

document.onmousemove = e => this.showMessage(e);

Here’s another example. It turns out that TypeScript has a strange way of initializing methods and fields. Basically, by the time you get around to calling object constructors, you can depend on methods to have been overridden correctly, but you can’t depend on fields. For instance, take a look at this code:

class User {
    constructor () {
        console.log("Field from: " + this.field);
        console.log("Method from: " + this.method());
    }
    field: string = "User class";
    method(): string { return "User class"; }
}

class RegisteredUser extends User {
    field: string = "RegisteredUser class";
    method(): string { return "RegisteredUser class"; }
}

var registeredUser = new RegisteredUser();

In my opinion, it would make most sense to have this output:

Field from: RegisteredUser class
Method from: RegisteredUser class

Failing that, this would at least be consistent:

Field from: User class
Method from: User class

But instead, this is what we get:

Field from: User class
Method from: RegisteredUser class

And of course, that’s not at all intuitive. In looking at the emitted JS, it’s clear why this happens: methods are initialized (i.e., assigned to the class prototype) when the class is constructed, but fields aren’t initialized until the object constructors are called, and those get called in the order superclass->subclass.

var __extends = this.__extends || function (d, b) {
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var User = (function () {
    function User() {
        this.field = "User class";
        console.log("Field from: " + this.field);
        console.log("Method from: " + this.method());
    }
    User.prototype.method = function () {
        return "User class";
    };
    return User;
})();
var RegisteredUser = (function (_super) {
    __extends(RegisteredUser, _super);
    function RegisteredUser() {
        _super.call(this);
        this.field = "RegisteredUser class";
    }
    RegisteredUser.prototype.method = function () {
        return "RegisteredUser class";
    };
    return RegisteredUser;
})(User);
var registeredUser = new RegisteredUser();

So when you call the RegisteredUser() constructor, the correct methods have already been wired up correctly to its prototype, but the fields haven’t been: so the User() constructor calls the right methods, but doesn’t call the expected fields. That’s understandable when you look at the emitted JavaScript, but not at all intuitive. Basically it means that the same exact field reference in the same method can sometimes be referring to a field from the local class, and sometimes to a field from the subclass, depending on where you’re calling it from. That’s a pretty serious violation of the “law of least astonishment”.

Even more confusing, however, are some weirdnesses having to do with module loading. There are several different ways to handle dependencies between modules. One way is to just specify them at design-time in a ///<reference /> tag. So if you had a User.js file that looked like this:

class User {
    name: string;
    createdOn: Date;
}

You could then have a RegisteredUser.ts file that looked like this:

///<reference path="User.ts" />
class RegisteredUser extends User {
    registeredOn: Date;
}

But then you have to include both “User.js” and “RegisteredUser.js” in different <script> tags on your web page – which isn’t what you want to do if, say, you’ve got dozens of files and you’re trying to provide an API for lots of external users to use and you’re almost certainly going to be refactoring and changing the module names regularly.

The other way is to use named modules. The way you do this is a little odd if you haven’t worked with JavaScript loaders like tiki (which uses the CommonJS format) or curl and RequireJS (which use the somewhat more complicated and flexible AMD format). You’d modify your “User.ts” file by adding an “export”:

export class User {
    name: string;
    createdOn: Date;
}

And then you’d modify your RegisteredUser to import that module, which then acts as a sort of namespace prepending the exported “User” class:

import mUser = module("User");
export class RegisteredUser extends mUser.User {
    registeredOn: Date;
}

Assuming you’re using the AMD format, the compiled code for User.js looks like so:

define(["require", "exports"], function(require, exports) {
    var User = (function () {
        function User() { }
        return User;
    })();
    exports.User = User;    
})

And the compiled code for RegisteredUser.js:

var __extends = this.__extends || function (d, b) {
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
define(["require", "exports", "User"], function(require, exports, __mUser__) {
    var mUser = __mUser__;

    var RegisteredUser = (function (_super) {
        __extends(RegisteredUser, _super);
        function RegisteredUser() {
            _super.apply(this, arguments);

        }
        return RegisteredUser;
    })(mUser.User);
    exports.RegisteredUser = RegisteredUser;    
})

All of those “define” calls look weird, but it basically means that the module loading gets offloaded to (say) RequireJS. But if you want to use any of these classes on a web page, there’s another step you have to go through, which is to require() them on the web page itself, like so:

<script type="text/javascript" src="../Scripts/require.js"></script>
<script type="text/javascript">
    require(['User', 'RegisteredUser'], function (mUser, mRegisteredUser) {
        var user = new mUser.User();
        var registeredUser = new mRegisteredUser.RegisteredUser();
    });
</script>

If you’re used to a nice, clean build and dependency system like C# gives you, all that’s a bit much to wrap your head around. And you have to jump through a few more hoops if you want to give users of your library a decent experience. But it’s not the weird part. The weird bit comes when you start trying to mix all this module loading stuff with interfaces. Specifically, this is  a problem I ran into when I was working with trying to get SignalR working with TypeScript. I had a “Service.ts” file that looked something like this:

///<reference path="../Scripts/jquery-1.8.d.ts" />
///<reference path="../Scripts/signalr-1.0.d.ts" />

interface SignalR {
    roomHub: Service.RoomHub;
}

module Service {
        export var roomHub = $.connection.roomHub;
        export interface RoomHub { }
}

And that worked fine. But then I needed to “modularize” it, so that I could access it from other files, so I added an “export” on the module bit, like so:

///<reference path="../Scripts/jquery-1.8.d.ts" />
///<reference path="../Scripts/signalr-1.0.d.ts" />

interface SignalR {
    roomHub: Service.RoomHub;
}

export module Service {
        export var roomHub = $.connection.roomHub;
        export interface RoomHub { }
}
And suddenly the compiler informed me that “roomHub” wasn’t a member of “$.connection.roomHub”. I’m honestly not sure if this is a compiler bug, or some expected but entirely unintuitive side-effect of modularization. And it took me quite a while to figure out the workaround: to move my interface definitions into a separate file (“ISignalR.ts”):
interface SignalR {
    roomHub: RoomHub;
}

interface RoomHub {
}

And then reference that file from the file that contains the exported Service module:

///<reference path="../Scripts/jquery-1.8.d.ts" />
///<reference path="../Scripts/signalr-1.0.d.ts" />
///<reference path="ISignalR.ts" />

export module Service {
    export var roomHub = $.connection.roomHub;
}

Apparently the rule is something like: if your file exports anything, you can’t have any interfaces in it that extend interfaces that don’t originate in the file with the exports. I’m not sure if that’s precisely it, or if it makes sense to have that requirement – but I can’t seem to make it work any other way. (And trust me, I spent hours trying.)

(4) TypeScript is still way, way better than JavaScript

So TypeScript is very new and fairly raw, with some significant rough edges. But there’s still no doubt in my mind that it’s a much, much, much better language than JavaScript. I haven’t experimented enough with CoffeeScript or Dart to be able to speak intelligently about how it stacks up to those alternatives. But at this point, I really can’t imagine going back to normal JavaScript for web development. Why would anyone? And when it finally catches up to languages like C# (or maybe even brings in some functional features from F#) – man, web development might actually start being fun.

242 comments:

«Oldest   ‹Older   201 – 242 of 242
Anonymous said...

Excellent items from you, man. I have consider your stuff prior to and you're simply extremely wonderful. I really like what you've
acquired here, certainly like what you're saying and the way in which through which you assert it. You're making it enjoyable and you continue to care for
to keep it wise. I cant wait to learn much more from you. This is actually a great site.


Feel free to visit my page ... buy guaranteed facebook likes

Anonymous said...

You actually make it appear really easy together with your presentation but I find this matter to be really one
thing which I think I would by no means understand. It sort
of feels too complex and very huge for me. I am looking ahead in your subsequent submit,
I will try to get the cling of it!

Here is my web site ... buy facebook likes

Anonymous said...

Usually I don't read article on blogs, however I would like to say that this write-up very pressured me to try and do so! Your writing style has been surprised me. Thank you, very nice article.

Feel free to visit my website - ルイヴィトンメンズ

Anonymous said...

7%, cheapest car insurance cheapest car insurance up from $7.
Get several quotes and have a peace of mind to anyone worried about losing their boiler or water
supply in the dead of winter.

My page ... bestcarinsurancerates.Org

Anonymous said...

There's definately a great deal to know about this topic. I really like all the points you have made.

My site rozliczenie przez internet

Anonymous said...

Because some dog insurance companies will refuse to provide insurance for a few months earlier and accepted the
premium. Paper towels are a wood by-product and can
scratch acrylic. Pinnacol Assurance's decision to car insurance quotes online bail out Fannie Mae and Freddie Mac, and now I am left with the debt. Upon installation, your truckis ready to accept industrial projects. This is a wonderful way to boost your car insurance quotes online self-esteem and confidence in yourself.

Feel free to surf to my web blog; cheap Auto insurance

Anonymous said...

This is a topic which is close to my heart...
Many thanks! Exactly where are your contact details though?


my weblog ... monster beats

Anonymous said...

Fantastic beat ! I wish to apprentice while you amend your web site, how could i subscribe
for a blog web site? The account helped me a acceptable deal.
I had been a little bit acquainted of this your broadcast offered bright clear concept

my webpage ... グッチアウトレット

Anonymous said...

What i do not realize is if truth be told how you are no longer
actually a lot more smartly-liked than you may be now. You are very
intelligent. You know therefore considerably relating to this matter, made me in my opinion imagine it from a
lot of various angles. Its like women and men are not interested
except it is something to accomplish with Lady gaga!

Your individual stuffs excellent. At all times take
care of it up!

Also visit my web blog :: wypełnij pit 37

Anonymous said...

You really make it seem really easy together with your presentation however I
find this matter to be actually something that I believe
I'd never understand. It seems too complex and very huge for me. I'm looking forward in your next publish,
I'll try to get the cling of it!

My webpage ... http://www.onebeerbet.com

Anonymous said...

Howdy! I simply want to give an enormous thumbs up for the great info you have here on this post.
I might be coming back to your blog for more soon.



My web page pemandangan kota seoul korea selatan

Anonymous said...

WOW just what I was looking for. Came here by searching for namoro

My weblog - http://backgroundrevealed.com/

Anonymous said...

Not only does it feature a near-silent, beautiful and spacious
interior, but it will raise your Online Auto Insurance
Quote rates. At a telematics conference in suburban Detroit last week, Allstate's Mr. online auto insurance quote is mandatory for drivers in the circumstance of applying in groups. Injury claims, death and legal costs are covered by liability car insurance plans. An Example of Umbrella CoverageTypically, an umbrella policy at work: Let's say you have a life insurance policy through your work if you're lucky or had good financial advice.

Anonymous said...

My sis instructed me about your web site and how great it is.

She’s right, I am really impressed with the writing and slick design.

It seems to me you’re simply scratching the floor by way
of what you may accomplish, but you’re off to an amazing
begin!

My web site: why can i not get pregnant

Anonymous said...

This month, the Pentagon stated that it was checking off all the right marks,
but Lightning doesn't always feel like a major improvement and it absolutely didn't slow down
performance. On top of that it's very easy to use while hiking. When old machines are compared with the new notification options in the Reminders application, you can expect that any decent property manager will be calling you for a date. fleshlight latest sensation in fleshlight tin!

Anonymous said...

Prepping and Using Your fleshlight Adult ToyThe proper prep can make a claim.
The key point that the lawsuit completely ignores or
perhaps fails to understand is how new media, new technologies, and the kind of person you
are.

Anonymous said...

12 scRnd 5: sc in next 3 sc, 2 sc inlast sc, ch 1, do not turn.
7 is technically not a clean install will not be possible, and will spend long periods wrapped around a single branch.


Also visit my blog post fleshlight

Anonymous said...

Wonderful article! This is the kind of information that are supposed to be shared around the internet.
Shame on Google for not positioning this put up upper! Come on over and
discuss with my web site . Thank you =)

My blog post - luxe vakantiehuisjes frankrijk huren

Anonymous said...

A growing number of insurers now offer a feature designed to help the aaa auto insurance discount industry can have five times the higher rates of the next province to Ontario?

Anonymous said...

So how do you get your ex how to get a girlfriend fast back and perceive that there is no longer
applicable. When you've been talking to for half an hour. So, where are you now? You do not prefer to attempt and win back their how to get a girlfriend fasts is actually counter productive to what they are doing this intentionally. Well if you have the right strategy, then your chances of getting your ex boyfriend, how to get a girlfriend fast, husband, wife or husband back into your life.

my blog post ... how to get girlfriend

Anonymous said...

May I just say what a relief to discover somebody who genuinely understands what they're discussing online. You definitely know how to bring an issue to light and make it important. More and more people really need to look at this and understand this side of the story. I was surprised you are not more popular given that you definitely possess the gift.

Review my webpage; pity

Anonymous said...

The type of slippery shake blender is just about the generally
functional items you is capable of having with your
your kitchen's. Blenders, you will find, liquefy as well as , puree great list of ingredients and mix using our own chemical. Through the potent power plant to positively modern theme, currently its meant aim as being a utility vehicle.

my website ... wheatgrass juicer walmart

Anonymous said...

Download my free ebook "I've Found the Fountain of Youth- Let Me Show You Too. There are many good muscle building and weight gaining programs that you can buy and download from the internet. s true that the Internet is filled with a lot of information and tips about getting the kind of body you want however most of us find it very challenging just to be able to stick to a weight gain training program that will eliminate the question of how to gain weight for women once and for all.

Here is my web blog ... how to gain weight in a month ()

Anonymous said...

If you answered Yes to two or maybe more with the questions
above, then chances are you could possibly get approved for any
refinancing option by any local bank or private lender payday loans on line apart from that, you should be the resident of united kingdom.

Anonymous said...

I don't want to have into the economics or politics of attitudes of this kind, but simply claim they are very important in influencing policy, a posture I think David Glasner supports here personal loans for bad credit increasing your wages, whether via a raise or side income, can be an under-sung method of helping lower debt.

Anonymous said...

Good day I am so delighted I found your blog page, I really found
you by mistake, while I was searching on Yahoo for
something else, Anyhow I am here now and would just
like to say thanks for a tremendous post and a all round entertaining blog (I also love the theme/design),
I don’t have time to read through it all at the minute but I have book-marked
it and also included your RSS feeds, so when I have time I will be back
to read much more, Please do keep up the great b.



my homepage :: pity2014

Anonymous said...

It is pretty important to focus or concentrate
in a proven marketing formula and work all you can on that.
Whereas before they wouldn't have been considered a viable market, they are now able to access the internet and dabble in small business. There are so many successful online businesses these days that thrived and grew simply because they turned to online marketing services to take care of their marketing plans and visions.

my weblog: shopping

Anonymous said...

We are a bunch of volunteers and opening a brand new
scheme in our community. Your site offered us with helpful info to
work on. You've done an impressive process and our entire group might be thankful to you.

my page ... druk pit 37

Anonymous said...

I must thank you for the efforts you have put in penning this blog.

I really hope to see the same high-grade blog posts from you later on
as well. In truth, your creative writing abilities has inspired me to get my own,
personal site now ;)

my web site :: zryczałtowany podatek dochodowy

Anonymous said...

Ѕtress аnxiеty attackѕѕ are primаrilу ρhysiological.
I hope thаt this one ωеіrԁ anхietу attacks rеmedy will help уou immensely anԁ thаt іs
alωаys baԁ neωs. With some gentle
techniques to explοre сonѕсiouѕness, the ѕtudent leaгnѕ to hаndlе аnxiety аttаckѕ, angeг аnԁ sadneѕs
may аctually be fеar. Lots of ρеoρle gеt sο freaκed out,
theу run tο the e.

Look at mу weblog - what is anxiety disorders

Anonymous said...

The Istituto Marangoni's campuses are in the fashion capitols of the world, thus providing students with an up close and personal experience of fashion. If you still wish to attend, a suitable alternative avenue to explore is to tap the ATRs in the European Union who may have stock of unsold tickets lying with them. Why settle for mediocrity when you could enroll in a program that is rated one of the top 10 Executive MBAs in the world.

my web page ... move to london

Anonymous said...

There are so many areas of the capital which fairly vibrate with excitement and vibrancy.
If you still wish to attend, a suitable alternative avenue to explore
is to tap the ATRs in the European Union who may have stock of unsold
tickets lying with them. His Latest Project - Free Online Dating
Services Shows How The Power Of Online Dating Can Be Harnessed Internationally
and With Great Success, Or You Could Post Your Valued Comments On His Blog
At London Dating.

Also visit my blog post - move to london

Anonymous said...

I really like reading a post that will make people think.
Also, many thanks for permitting me to comment!


Also visit my web-site :: how to find a sugar daddy

Anonymous said...

My husbаnd and І knew I ωаnted to
know. Each book is a jοurnеy іn itѕelf, but it
all rеsts ρurеly upon the twists and turns of my imаgination.

Ѕo, I ask you - how is a woman like a pіano?
ladіes handbagѕ wіth а Vintage
Flair and or Ruffles" then it will take you to where you can click on slideshow and view the photos in a slideshow format.

my web-site: tote bags

Anonymous said...

Thanks on your marvelous posting! I really enjoyed reading it, you can be a great author.

I will make sure to bookmark your blog and definitely will come back in the foreseeable future.
I want to encourage continue your great job, have a nice morning!


my website; ulga podatkowa 2014

Anonymous said...

So, if you want a diverse and balanced work experience while on your hotel management courses,
London offers just that. To top that deal, you also are free
to choose from between thirty or so muffins working every night
in your post code. Why settle for mediocrity when you could enroll in a program that is rated one
of the top 10 Executive MBAs in the world.

my web blog: moving to london

Anonymous said...

I know this website offers quality depending posts and other
stuff, is there any other site which presents these information in quality?


My webpage ... sugar daddy website

Anonymous said...

He takes your roofing needs seriously, and is well aware that a strong,
durable roof can help protect both your family and your employees.
Making the right choice can save you many problems later on including during the job itself.
If time is a priority for you, ask how soon a project could be started.


Also visit my page Boca condo roofing companies

Anonymous said...

car insurance fraud cases providers typically
divide drivers into a three-tiered system of risk: preferred risk, standard risk, and non-standard risk.
Here are some quick tips that help to find
the appropriate coverage for your needs, but something
that will just end up being worthless to you.
This is why it is so important to have a chance at the win, you first need to decide that you can confront if there's any ill-fated incident.

Also visit my web page; auto insurance

Anonymous said...

car insurance fraud cases providers typically divide drivers into
a three-tiered system of risk: preferred risk, standard risk, and non-standard risk.
Here are some quick tips that help to find the appropriate
coverage for your needs, but something that will just end up being worthless to you.

This is why it is so important to have a chance at the win, you first need to decide that you can
confront if there's any ill-fated incident.

My page: auto insurance

Anonymous said...

Wearing new sandals was also part of the hippie look each men and women
of all ages. Do not talk in a definite hurried manner and for rudely to suer.
Both men and women wore frayed bell-bottomed jeans,
tie-dyed shirts, workshirts, and headbands. Guests never know what
Mulch, Sweat Shears will do and vice versa. http://mail.
english-channel.tv/groups/lejbugs/wiki/5f673/The_Anxiety_And_Its_Impact_Working_People_Of_Call_Center.
html

My page: balenciaga

Anonymous said...

Hi, i think that i noticed you visited my blog thus i came to go back the
choose?.I'm trying to in finding issues to enhance my web site!I suppose its adequate to make use of a few of your concepts!!

Here is my web site live roulette *www.casinoroulette.co*

«Oldest ‹Older   201 – 242 of 242   Newer› Newest»