I'm writing a little type guarding tool, and am trying to come up with a way to split every possible type into a few types that cover every possible type, making sure there's no overlap.
I know in JavaScript primitive types are a thing, so maybe I could start with primitive and non-primitive types. Primitives are string, number, bigint, boolean, symbol, null and undefined, so I can define a type guard like:
type Primitive = string | number | bigint | boolean | symbol | undefined | null;
function isPrimitive(val: unknown): val is Primitive {
return typeof val === 'string'
|| typeof val === 'number'
|| typeof val === 'bigint'
|| typeof val === 'boolean'
|| typeof val === 'symbol'
|| val === undefined
|| val === null;
}
Now I want the complement of that. On the JavaScript side, I know typeof val === 'object' won't help because that'd include null. But val instanceof Object seems to work. It returns false for everything isPrimitive returns true, and it seems to return true for everything else:
function isNotPrimitive(val: unknown): val is Object {
return val instanceof Object;
}
The problem is that val is Object is broader than what the function returns because the TypeScript Object type seems to include primitives:
type NumberIsObject = number extends Object ? 'Yes' : 'No'; // "Yes"
So is there a type in TypeScript that corresponds to things that are instances of Object? Or if not, is there a better way to group types into broad categories that will satisfy my criteria?