- Update 2022-07-10: new approach based on
Function.prototype.hasInstance()
In principle, the rule “typeof is for primitive values, instanceof is for objects” works. There are two exceptions:
- Checking if a value is an object
- Cross-realm instance checks
Neither typeof nor instanceof are good at checking if a value is an object.
Checking if a value is non-primitive via typeof, is complicated:
const isObject = (v) => (
v !== null && (typeof v === 'object' || typeof v === 'function')
);instanceof can only be used for objects whose prototype chains end with Object.prototype:
> Object.create(null) instanceof Object
false
> typeof Object.create(null)
'object'
> Object.prototype instanceof Object
false
> typeof Object.prototype
'object'
v instanceof C only works if both v and C come from the current realm. In other cases, we can’t generally perform the check, but for some classes, there are workarounds:
Array.isArray(v)typeof v === 'function'
The proposal introduces two features for cross-realm instance checks:
Symbol.typeMarkerlets us assign cross-realm symbols as type markers for classes.- All built-in classes get type markers.
Function.prototype.hasInstance(v)is inherited by all functions and checks ifvis an instance ofthis.
The proposal also introduces one static method:
Object.isObject()
These are my first thoughts. I’m not attached to either specific names or many of the details.
To check if a value is primitive, use typeof and ===:
const categorize = (v) => ({
isUndefined: v === undefined,
isNull: v === null,
isBoolean: typeof v === 'boolean',
isNumber: typeof v === 'number',
isBigint: typeof v === 'bigint',
isString: typeof v === 'string',
isSymbol: typeof v === 'symbol',
});To check if a value v (that may or may not come from another realm) is an instance of a class C, use C.hasInstance(v):
Map.hasInstance(valueFromAnotherRealm)
MyClass.hasInstance(valueFromAnotherRealm)
// Etc.To determine if a value is an object, use Object.isObject():
> Object.isObject(Object.create(null))
true
> Object.isObject(Object.prototype)
true
More examples: see tests.
- Should the custom matchers proposed by ECMAScript Pattern Matching support
Symbol.typeMarker?
I like the idea!
It seems you have to know the logic behind to make a good use of it though.
Why
hasType(v, 'number')and nothasType(v, Number)orhasType(v, Array)and nothasType(v, 'array')?In a way we are back to the initial problematic:
Depending on what kind of type check we want to perform, we have to choose between