AS3 casting String to Number?
Converting FlashVars parameters to properties is an example where you are forced to deal with strings and sometimes these values need to be numbers. I have been annoyed with with my own inconsistencies with how I test to see if the FlashVar casts correctly or should be rejected (because it had a character other than a number in the string). I was discovering that some techniques were giving false negatives. So I spent some time of trying to establish a best practice.
I considered these four techniques for casting a string to a Number:
- parseInt("1234");
- new Number("1234");
- "1234" as Number;
- Number("1234");
Below is the AS3 code and the trace results which I used to base my conclusions here.
Results:
Using "as Number" to cast a string to a Number looks like bad news to me. All other techniques preformed as expected. Casting a string such as "foo" using "as Number" to cast is to a strongly typed variable results with a false value when isNaN() is applied to it. This is the false negative I mentioned before. Even casting the string "1337" using "as Number" results in the value 0 which not only fails the falsies ternary test but is wrong.
Conclusions
- don't use "as Number" to cast a string to a Number.
- Using a "falsies ternary test" is not a great approach for string to number validation since "0" is a valid number and will correctly fail this test. Maybe that works well for your logic anyhow.
Discussion
Casting is perhaps the wrong term since I am converting a primitive to an object which isn't true polymorphism, but perhaps string is being treated as an object.
I talked to Ryan Frishberg at the recent 360|Flex in Indianapolis about this. Ryan works on the Flex SDK team at Adobe. He said that "new Number('foo')" make use of conversion methods that are not available to the technique "'foo' as Number" which will evaluate to null since this casting is not possible. String can't be a Number. Ok, that is the d'oh moment for me. Should know better.
It's problematic though, because it fails to report a run-time or compile-time error.
-
var n1a:Number = parseInt("foo");
-
var n2a:Number = new Number("foo");
-
var n3a:Number = "foo" as Number;
-
var n4a:Number = Number("foo");
-
-
var n1b = parseInt("foo");
-
var n2b = new Number("foo");
-
var n3b = "foo" as Number;
-
var n4b = Number("foo");
-
-
var n1c:Number = parseInt("1337");
-
var n2c:Number = new Number("1337");
-
var n3c:Number = "1337" as Number;
-
var n4c:Number = Number("1337");
-
-
trace (".............. strong typed 'foo'");
-
trace ("n1a: " + n1a);
-
trace ("n2a: " + n2a);
-
trace ("n3a: " + n3a);
-
trace ("n4a: " + n4a);
-
trace (".............. not strong typed 'foo'");
-
trace ("n1b: " + n1b);
-
trace ("n2b: " + n2b);
-
trace ("n3b: " + n3b);
-
trace ("n4b: " + n4b);
-
trace (".............. Strong typed '1337'");
-
trace ("n1c: " + n1c);
-
trace ("n2c: " + n2c);
-
trace ("n3c: " + n3c);
-
trace ("n4c: " + n4c);
-
trace (".............. isNaN n1");
-
trace ("n1a: " + isNaN(n1a));
-
trace ("n1b: " + isNaN(n1b));
-
trace ("n1c: " + isNaN(n1c));
-
-
trace (".............. isNaN n2");
-
trace ("n2a: " + isNaN(n2a));
-
trace ("n2b: " + isNaN(n2b));
-
trace ("n2c: " + isNaN(n2c));
-
-
trace (".............. isNaN n3");
-
trace ("n3a: " + isNaN(n3a));
-
trace ("n3b: " + isNaN(n3b));
-
trace ("n3c: " + isNaN(n3c));
-
-
trace (".............. isNaN n4");
-
trace ("n4a: " + isNaN(n4a));
-
trace ("n4b: " + isNaN(n4b));
-
trace ("n4c: " + isNaN(n4c));
-
-
trace (".............. falsies ternary test");
-
(n1a)?trace("n1a: true"):trace("n1a: false");
-
(n2a)?trace("n2a: true"):trace("n2a: false");
-
(n3a)?trace("n3a: true"):trace("n3a: false");
-
(n4a)?trace("n4a: true"):trace("n4a: false");
-
-
(n1b)?trace("n1b: true"):trace("n1b: false");
-
(n2b)?trace("n2b: true"):trace("n2b: false");
-
(n3b)?trace("n3b: true"):trace("n3b: false");
-
(n4b)?trace("n4b: true"):trace("n4b: false");
-
-
(n1c)?trace("n1c: true"):trace("n1c: false");
-
(n2c)?trace("n2c: true"):trace("n2c: false");
-
(n3c)?trace("n3c: true"):trace("n3c: false");
-
(n4c)?trace("n4c: true"):trace("n4c: false");
-
.............. strong typed 'foo'
-
n1a: NaN
-
n2a: NaN
-
n3a: 0 //unexpected
-
n4a: NaN
-
.............. not strong typed 'foo'
-
n1b: NaN
-
n2b: NaN
-
n3b: null //unexpected
-
n4b: NaN
-
.............. Strong typed '1337'
-
n1c: 1337
-
n2c: 1337
-
n3c: 0 //unexpected
-
n4c: 1337
-
.............. isNaN test
-
n1a: true
-
n2a: true
-
n3a: false //unexpected
-
n4a: true
-
n1b: true
-
n2b: true
-
n3b: false //unexpected
-
n4b: true
-
n1c: false
-
n2c: false
-
n3c: false
-
n4c: false
-
.............. falsies ternary test
-
n1a: false
-
n2a: false
-
n3a: false
-
n4a: false
-
n1b: false
-
n2b: false
-
n3b: false
-
n4b: false
-
n1c: true
-
n2c: true
-
n3c: false //unexpected
-
n4c: true

the "as" operator isn't a conversion function like the others. It checks to see if the value used with as is of the type supplied. If so, that value is returned. If not, null is returned. If null is returned, the actual resulting variable value can differ depending on its type since, for example, Number-typed variables cannot be null, only numeric values and NaN (likely null would be converted to 0).