JScript .NET Type Coercion Semantics, Part Four: Coercion at last

Before I get going, a couple notable milestones. 

First, this is post number 200! Who would have believed that I'd have so much to ramble on about? ("Anyone who knows you" would be the correct answer to that rhetorical question I suppose.)

Second, as of Sunday I am now over (cue Dr. Evil) one billion seconds old. Happy gigasecond to me, happy gigasecond to me…

OK, enough frivolity. Back to boring facts about JScript .NET's coercibility algorithm.

**************************

Today I'll finish up and give a more precise definition of how JScript .NET determines whether a particular value is coercible to a given type. Then we'll get back into lighter topics.

Suppose we have a value V which we wish to coerce to type T without data loss or error. For clarity I'll split the algorithm up into two sections, one for "special" types like classes, arrays and enumerations, and one for "primitive value" types like integers and strings.

Suppose T is a reference, enumerated, array, etc, type:

  • If V is null or undefined then V is coercible to T irrespective of T.
  • If T is System.Object then V is coercible to T irrespective of V.
  • If T is a class and V is an instance of T (which includes being an instance of a subclass of T) then V is coercible to T. 
  • If T is an interface and V implements T then V is coercible to T.
  • If T is a non-JScript array of known element type E and V is a JScript array then V is coercible to T if and only if every element of V is coercible to E.
  • If T is a JScript array and V is a rank-one non-JScript array then V is coercible to T.
  • If T is an enumerated type and V is a member of enumerated type E then V is coercible to T if and only if T and E are the same type.
  • If T is an enumerated type and V is a string and then V is coercible to T if and only if V names a member of T.
  • If T is an enumerated type of implementation type E and V is not a member of an enumerated type then V is coercible to T if and only if V is coercible to E.
  • If T is the System.Type type and V is a class name (not an instance of a class but actually the name of a class) then V is coercible to T.
  • If T is a delegate type and V is a function object (including a closure) then V is coercible to T if and only if V has the same function signature as the delegate.
  • If T defines an appropriate type coercion function then V is coercible to T if and only if the function call doesn't throw an exception.

Now let's consider the primitive type coercion rules.

JScript .NET supports several numeric types: 8, 16, 32 and 64 bit integers in both signed and unsigned flavours, 32 and 64 bit floats and a decimal type. JScript .NET also supports a "char" type which is an unsigned 16 bit integer that holds a single Unicode UTF-16 value. JScript .NET also supports a scalar Date/Time type which is treated the same as a signed 64 bit integer unless otherwise noted.

Note that data loss is allowed when coercing anything to Boolean and when coercing strings to numbers.

  • If T is the type of V then V is coercible to T. (Duh!)
  • If V is undefined or null then V is coercible to T irrespective of T. (FYI, coercing V to Boolean goes to false. Coercing V to any numeric type goes to 0. Coercing V to string goes to "".)
  • If V is Boolean then V is coercible to T irrespective of T. (FYI, coercing a V to a numeric type goes to 0 or 1, coercing V to string goes to "true" or "false".)
  • If V is char then V is coercible to T irrespective of T. (FYI, coercing V to Boolean goes to false if char is \u0000, true otherwise. Coercing V to numeric goes to the UTF-16 value. Coercing V to string goes to a single-character string.)
  • If T is any numeric type and V is a member of any numeric type then V is coercible to T if and only if V can be coerced to T without overflow or loss of precision. For instance, if V were the unsigned 8-byte integer 300 then V could be coerced to an unsigned 2-byte integer regardless of the fact that the 8-byte unsigned integer type is not promotable to the 2 byte integer type. Note that if V is, say, the 8-byte float value 0.1 then this is not coercible to a 4-byte float as there will be a loss of precision. (0.5 would be coercible.) Recall that the assignability algorithm has a special case to ensure that compile-time constants inferred to be 64 bit floats are assignable to 32 bit floats even if they are not coercible.
  • If T is string and V is of any numeric type then V is coercible to T. (JScript .NET uses the standard number-to-string routine. If V is a Date/Time scalar then it uses the appropriate date string.)
  • If T is Boolean and V is of any numeric type then V is coercible to T. (If V is zero or NaN then it goes to false, otherwise it goes to true.)
  • If T is Boolean and V is a string then V is coercible to T. (If V is "" then it goes to false, otherwise true.)
  • If T is a date type and V is a string then V is coercible to T if and only if V is parsable as a date.
  • If T is char and V is a string then V is coercible to T if and only if V is a single-character string.
  • If T is a numeric type and V is a string that can be parsed as type T then V is coercible to T
  • If T is a numeric type and V is a string that cannot be parsed as type T then V is coercible to T if and only if V is parsable to an 8-byte float and the resulting float value is coercible to T.

If none of the above apply, V is not coercible to T, and will likely cause a type mismatch exception.