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.)
export type Something =
number | string | boolean | symbol | object | bigint;
export function IsSomething(value: unknown) : value is Something {
return value !== undefined && value !== null;
}
let a = [1, "2", 3];
Narrowing(a);
NotNarrowing(a);
function Narrowing(o: unknown) { // OK
if (IsSomething(o)) {
console.log(o.toString(), o.constructor);
}
}
function NotNarrowing(o: unknown) { // NOT OK
if (o !== null && o !== undefined) {
console.log(o.toString(), o.constructor);
}
}
In TypeScript, every value except for null
and undefined
is assignable to the so-called empty object type {}
. So you can simplify your Something
to just {}
and your user-defined type guard function will work the same as before:
export type Something = {};
function isSomething(value: unknown): value is Something {
return value !== undefined && value !== null;
}
function narrowing(o: unknown) { // OK
if (isSomething(o)) {
console.log(o.toString(), o.constructor);
}
}
Furthermore, TypeScript 4.8 will introduce improved support for narrowing unknown
, after which your "NotNarrowing" function will start working, because checking (o !== null && o !== undefined)
will narrow o
from unknown
to {}
automatically:
// TS4.8+
function nowNarrowing(o: unknown) { // okay
if (o !== null && o !== undefined) {
console.log(o.toString(), o.constructor);
}
}