As a devoted follower of Anders (Hallowed be His name) I have been waiting to try TypeScript for some time. Since we could not agree to use it as the frontend language for our current application I had to try it on my own. I decided to port a small project for drawing Bézier curves I had done in Silverlight a few years ago. It seemed a good candidate since it was short (several hours of work), was a real task (if drawing Bézier curves is a real task) and provided the opportunity to explore the Canvas API, something I had been intending to do for some time.

    You can read the article about the original project here. The TypeScript version is not a literal port. It contains changes to better fit development in TypeScript as opposed to C# and some changes which come from the way I write code now as opposed to the time when I was a junior developer.

Your browser does not support Canvas element.
You can add a control point by double clicking, remove control point by right clicking and drag control points around.

The Code

    The code is available on GitHub (I used the opportunity to experiment with that too). As soon as I started porting I realized that the greatest challenge with JavaScript as opposed to C# is achieving encapsulation. So I decided to go out of my way to write code with somewhat proper encapsulation even if that made the code significantly more complex and do it in cases where I would not bother with it if I was writing JavaScript. I wanted to see how TypeScript helps with that.

    I decided to create a class Point as opposed to interface or just an object with x and y fields. I wanted to make this class immutable (a value type would be even better but JavaScript does not have them yet). I specifically created constructor and added getters with no setters. TypeScript compiles this to the syntax for defining property in ECMAScript 5 (which is basically calling a function to define property). While this ensures encapsulation and immutability in the statically typed world of TypeScript it does not ensure the encapsulation in JavaScript (or in TypeScript when using the type any). The private fields are in fact fully accessible to anyone willing to modify them. I decided to add a call to Object.freeze to ensure that the object is immutable. This does make the object immutable but surprisingly does not throw an error when the field is set to a new value. It just ignores the assignment. I guess a shit is still a shit even if you put perfume on it.

    I had a similar problem with the BezierCurve class. I wanted to encapsulate the control points array but I do not know a way to return the array in a form that prevents it from being modified. I ended up returning a copy to ensure the client code did not modify the internal state of the object. An IEnumerable interface would help here but I guess we will have to wait ten more years.

    If I have to single out one place where the code became significantly worse it is definitely the function which generates the curve points. In the original version it uses C# iterators (yield) to generate the points on demand. It is beautiful, short and effective code. In TypeScript I had to generate all points at once and put them in an array. I should probably revisit this when ECMAScript 6 is finalized and supported in all browsers and try to rewrite it with generators.

The Canvas API

    Compared to Silverlight the main difference with the Canvas API in HTML/JavaScript is that it is purely Bitmap one. In Silverlight you can just drop shapes on your canvas and modify them as you see fit at which point they are redrawn. It seems like this is not present in the web canvas by default. I ended up repainting the whole canvas whenever anything was updated. This is probably quite ineffective and I doubt Silverlight does that when one shape changes. On the other hand tracking repaint regions manually is not a trivial task. I wonder if different graphics libraries built on top of Canvas handle this for the user. If anyone has any experience with them I would like to hear how this problem is solved.

    I ran into an issue with offsetX and offsetY properties of the mouse click event which seem to be implemented according to the spec only in IE and are completely missing from Firefox. I had to stick some polyfill to get the whole thing working in Firefox.

    Of course the most stunning thing was that someone decided that the second mouse button is the middle one… go figure!

On a side note I have decided that one day I will develop my own drawing API where the function drawBezier will draw this:

Pierre Bézier
Pierre Bézier


    The generated JavaScript is definitely human usable. You can easily take the JS code and work with it as if you wrote it by hand. On the other hand the amount of syntactic noise needed to do simple encapsulation in JavaScript is absurd. While the idioms do exist I doubt I would use them in real world code if I was writing JavaScript. In this sense TypeScript does help a lot. You can write somewhat reasonable code without the burden of wrapping everything in functions all the time. In addition the type information does help when exploring an API like the Canvas API. Having intellisense saves a lot of digging into the docs especially for the events that are overloaded on strings. Obviously on a project as simple as this one I could not see any benefit from writing statically typed code but I feel like it would be pretty useful in the real world.

    Overall TypeScript seems a reasonable, reversible and therefore safe choice when going into the unpleasant world of JavaScript that we all have to visit more often than we would like.

Tags:   english programming 
Posted by:   Stilgar
15:42 12.08.2014


First Previous 1 Next Last 

Posted by   Stoicho (Unregistered)   on   23:08 12.08.2014

In strict mode ('use strict'), trying to set values to an immutable object will throw TypeError.

Posted by   Stilgar   on   23:31 12.08.2014

Hmm yes. I tested it but obviously I tested it wrong because now it does result in an error.

First Previous 1 Next Last 

Post as:

Post a comment: