- 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.typeMarker
lets 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 ifv
is 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