Suggestion
🔍 Search Terms
typeguard narrow infer implicit
✅ Viability Checklist
My suggestion meets these guidelines:
⭐ Suggestion
Some method for writing a typeguard that narrows based on Typescript’s regular type-narrowing rules, without having to specify the result of that narrowing. Syntax might be as narrowed, as in (x) => x is as narrowed. Since as is already a reserved TS keyword that must follow an expression, and cannot follow is, there is no possibility of this syntax being confused for any other valid TS expression. And x is is the already-implemented syntax for typeguards, keeping things consistent.
The goal with this syntax would be to write if (typeguard(x)) and have it behave exactly as if I had copied the body of typeguard into the if and replaced its arguments with x.
📃 Motivating Example
function isFooBarBaz(x: Foo): x is as narrowed {
return typeof x === 'object' && typeof x.foo === 'object' && typeof x.foo.bar === 'object' && x.foo.bar.baz !== undefined;
}
💻 Use Cases
The motivation is, currently we can write:
declare function setFooBarBaz(n: number): void;
interface Foo {
foo?: number | string | {
bar?: number | string | {
baz?: number
};
};
}
function maybeUpdateFooBarBaz(x: Foo) {
if (typeof x === 'object' && typeof x.foo === 'object' && typeof x.foo.bar === 'object' && x.foo.bar.baz !== undefined) {
setFooBarBaz(x.foo.bar.baz);
}
}
But if we want to make typeof x === 'object' && typeof x.foo === 'object' && typeof x.foo.bar === 'object' && x.foo.bar.baz !== undefined re-usable, we have to define a custom typeguard:
function isFooBarBaz(x: Foo): x is { foo: { bar: { baz: number } } } {
return typeof x === 'object' && typeof x.foo === 'object' && typeof x.foo.bar === 'object' && x.foo.bar.baz !== undefined;
}
function maybeUpdateFooBarBaz(x: Foo) {
if (isFooBarBaz(x)) {
setFooBarBaz(x.foo.bar.baz);
}
}
In addition to being verbose, this isn’t precisely typesafe—if the typeguard returns true, Typescript just assumes that x has this type, it doesn’t actually check that our typeguard has ensured that (beyond basic assignability checks to ensure that it is possible that x might have this type). In other words, it is my responsibility to ensure that if (isFooBarBaz(x)) is equivalent to the narrowing that Typescript does for me when I write if (typeof x === 'object' && typeof x.foo === 'object' && typeof x.foo.bar === 'object' && x.foo.bar.baz !== undefined).
The problem with that is, if I update the definition of Foo to allow the possible baz property to have the type string | number, but forget to update isFooBarBaz, Typescript will accept the assertion that baz is a number even though I only checked it was !== undefined, and it could be a string. I want a typeguard where the compiler would catch this, just as it does for the non-typeguard version.
Thus the proposal:
function isFooBarBaz(x: Foo): x is as narrowed {
return typeof x === 'object' && typeof x.foo === 'object' && typeof x.foo.bar === 'object' && x.foo.bar.baz !== undefined;
}
function maybeUpdateFooBarBaz(x: Foo) {
if (isFooBarBaz(x)) {
setFooBarBaz(x.foo.bar.baz);
}
}
By using x is as narrowed, my if(isFooBarBaz(x)) is actually identical to the original if (typeof x === 'object' && typeof x.foo === 'object' && typeof x.foo.bar === 'object' && x.foo.bar.baz !== undefined), and Typescript is handling its type in exactly the same way. I don’t have to determine (and maintain) the type signature of isFooBarBaz.
Suggestion
🔍 Search Terms
typeguardnarrowinferimplicit✅ Viability Checklist
My suggestion meets these guidelines:
⭐ Suggestion
Some method for writing a typeguard that narrows based on Typescript’s regular type-narrowing rules, without having to specify the result of that narrowing. Syntax might be
as narrowed, as in(x) => x is as narrowed. Sinceasis already a reserved TS keyword that must follow an expression, and cannot followis, there is no possibility of this syntax being confused for any other valid TS expression. Andx isis the already-implemented syntax for typeguards, keeping things consistent.The goal with this syntax would be to write
if (typeguard(x))and have it behave exactly as if I had copied the body oftypeguardinto theifand replaced its arguments withx.📃 Motivating Example
💻 Use Cases
The motivation is, currently we can write:
But if we want to make
typeof x === 'object' && typeof x.foo === 'object' && typeof x.foo.bar === 'object' && x.foo.bar.baz !== undefinedre-usable, we have to define a custom typeguard:In addition to being verbose, this isn’t precisely typesafe—if the typeguard returns
true, Typescript just assumes thatxhas this type, it doesn’t actually check that our typeguard has ensured that (beyond basic assignability checks to ensure that it is possible thatxmight have this type). In other words, it is my responsibility to ensure thatif (isFooBarBaz(x))is equivalent to the narrowing that Typescript does for me when I writeif (typeof x === 'object' && typeof x.foo === 'object' && typeof x.foo.bar === 'object' && x.foo.bar.baz !== undefined).The problem with that is, if I update the definition of
Footo allow the possiblebazproperty to have the typestring | number, but forget to updateisFooBarBaz, Typescript will accept the assertion thatbazis anumbereven though I only checked it was!== undefined, and it could be astring. I want a typeguard where the compiler would catch this, just as it does for the non-typeguard version.Thus the proposal:
By using
x is as narrowed, myif(isFooBarBaz(x))is actually identical to the originalif (typeof x === 'object' && typeof x.foo === 'object' && typeof x.foo.bar === 'object' && x.foo.bar.baz !== undefined), and Typescript is handling its type in exactly the same way. I don’t have to determine (and maintain) the type signature ofisFooBarBaz.