Double typecasts (and high-performance integer conversion)

Topics: General
Nov 15, 2013 at 1:23 PM
I just want to briefly share something very useful I just learned.

You may be familiar with the Math.floor('123') integer type-cast, which is useful for high-performance applications because it's generally much faster than e.g. parseInt('123'), and does essentially the same thing.

I wanted to do this in TypeScript, but I got an error:
var value = '123';

var int_value = Math.floor(<number> value); // FAILS
It won't let you type-cast a string to a number, which is sensible enough - however, it will let you cast an any to whatever you want, so this double typecast will do the trick:
var value = '123';

var int_value = Math.floor(<number> <any> value); // WORKS!
It looks a little odd, and it seemed dumb to me at first - but after thinking about it, I realized I like it - it's a double assertion, both for the compiler and for a programmer reading the source code: you're saying, "I know this is dangerous, but I know what I'm doing".

And since this is all happening at compile-time, the resulting code will be just as fast as the raw, no-guarantees, plain JavaScript version.

Passing a string to Math.floor() in JavaScript should raise questions, if the reader is observant enough to even notice that's what's happening - in TypeScript, you're required to explain and justify what you're doing, in this case with typecasts.

One of many reasons why I really like TypeScript :-)
Nov 15, 2013 at 3:46 PM
One could also get away with just
var value = '123';
var int_value = Math.floor(<any> value);
In this example the cast to any is perhaps not a bad thing. In general, however, we have a best practice rule of avoiding casting to any. The reason being, once that cast is in place the statement is effectively ignored by the compiler. This has a significant impact when refactoring code.

Here's a simple example.

First iteration
function foo(value:string){
    console.log(value.indexOf("a"));

}

class Boo { 
    indexOf = (char:string) => 1; 
}

// Client code somewhere in the codebase
var boo = new Boo();
foo(<any>boo); // Okay
Next iteration
// Ah, my boss told me to prefix all methods that return a value with "get", so let me see what I should do here
class Boo { 
    getIndexOf = (char:string) => 1; 
}

// Client code somewhere in the codebase
var boo = new Boo();
foo(<any>boo); // Still okay
After the second iteration the component crashes with "Uncaught TypeError: Object #<Boo> has no method 'indexOf'", in spite of everything compiling fine.

So for that reason it's best to always avoid casting to any.
Nov 18, 2013 at 12:17 AM
In this example the cast to any is perhaps not a bad thing. In general, however, we have a best practice rule of avoiding casting to any.
Absolutely agree - I would never suggest casting objects to <any> in this manner... that should raise a red flag, since you're effectively forfeiting compile-time type-checking; in which case you might as well be writing plain JavaScript.