Is there a way to make this TypeScript narrowing less ugly (or more elegant ;-) ❓
With IsSomething
type guard, I want to narrow down access
to common methods and properties of any JavaScript variable
which is not null
and not undefined
(things like .toString
, .valueOf
, .hasOwnProperty
, .constructor
etc.)
In TypeScript, the Object
interface is different from the primitive object
type, as the former is still assignable to any other primitive or complex type. It is also different from an empty type like type Something = {}
, as the latter doesn't supports IntelliSense.
// Object interface is OK
let a: Object = 42;
let b: Object = true;
let c: Object = "string";
let d: Object = Symbol(42);
let e: Object = new class SomeClass { someProp = 42 };
// Empty type is OK but no IntelliSense
let a1: {} = 42;
// Error: Type 'number' is not assignable to type 'object'
let a2: object = 42;
One subtle issue still remains. As @CertainPerformance pointed out, the above won't work for an edge case like Object.create(null)
, where an object is created which doesn't have any properties or methods, but isn't null
either.
If that's a requirement, the following should do the job:
// can be extended to check for toString, valueOf, etc
export function IsSomething(value: unknown) : value is Object {
return (<Object>value)?.constructor !== undefined;
}
Narrowing([1, 2, 3]);
function Narrowing(o: unknown) { // OK
if (IsSomething(o)) {
console.log(o.toString(), o.constructor);
}
}