This is a stage 0, "strawman" proposal.
typeof
has long been known to have perculiar behaviour. The most famously understood issue is that typeof null === "object"
, which consistently requires special casing when dispatching on a value's type. It was [proposed] 1 to correct typeof null
to return "null"
, but in the interests of the Harmony/"not breaking the web" initiative, it was decided that this behaviour should remain unfixed.
For the purposes of efficient specification, the ECMAScript specification actually defines a trivial algorithm [Type(x)
] 2 which has a more straightforward behaviour than typeof
. In particular:
null
has type Null; and- Functions have no special treatment - they are instances of Object.
Though desirable to programmers, Type(x)
is not available to ECMAScript programs.
A stated goal of the Reflect
module is to ["expose the essential methods that make up JavaScript's object model as defined by the spec"] 3. As such, it provides an obvious opportunity to make the specification's Type(x)
algorithm available directly to ECMAScript programs.
Reflect.type(x)
is proposed to accept as its sole argument any value x
and return a symbol that uniquely corresponds to the type of the value, as defined by the specification. Such values would be exposed in the Reflect.types
object, keyed by conventional spellings of the specification types, i.e. equivalent to:
Reflect.types = {
undefined: @@undefinedType,
null: @@nullType,
boolean: @@booleanType,
string: @@stringType,
symbol: @@symbolType,
number: @@numberType,
object: @@objectType,
}
where @@
denotes well-known symbols, referenced by algorithms of the specification.
A possible implementation of Reflect.type
would be equivalent to:
Reflect.type = function(x) {
switch(typeof x) {
case "undefined":
return @@undefinedType;
case "boolean":
return @@booleanType;
case "string":
return @@stringType;
case "symbol":
return @@symbolType;
case "number":
return @@numberType;
case "object":
case "function":
default: // NB: `typeof` may return arbitrary strings for implementation-defined values!
if (x === null) {
return @@nullType;
}
return @@objectType;
}
};
- Using symbols to represent each type as opposed to short strings as
typeof
does may prove useful in ensuring that the domain values oftypeof
andReflect.type
are never accidentally mismatched. - Symbol descriptions can aid debugging, as the default
Symbol#toString
implementation includes the description.
- How might new, user-definable value types be exposed? Assuming they become new primitives,
Reflect.type
should probably return an appropriate symbol, accessible from whatever mechanism is used to define the type. Alternatively, greater symmetry could be achieved by introducing global objectsNull
andUndefined
, that, alongside the existing globalsBoolean
,String
,Symbol
,Number
, andObject
, and user-defined value objects, would have atype
property holding the relevant symbol, such that e.g.Reflect.type(undefined) === Undefined.type
- Perhaps, for symmetry with
Object.getPrototypeOf
, it could be argued that this should be namedReflect.getTypeOf
. This is both more verbose and at odds with the current specification language.