Is this possible to have types restricted without if by function calls that never return for e.g undefined like assert in Typescript?
Example code:
interface Foo { bar(): void }
function getFoo(): Foo | undefined { }
function test() {
const foo = someService.getFoo();
assert(foo);
if (!foo) { // now mandatory because without this foo may be still undefined even if assert protects us from this
return;
}
foo.bar(); // , here foo may be undefined
}
I would like to be able to write assert in such way that i can skip following if (!foo) clause and have foo type restricted to plain Foo.
Is this possible in Typescript?
I've tried adding overloads with never for types that throw:
function assertGuard(v: undefined | null | '' | 0 | false): never;
function assertGuard(v: any): void; // i'm not sure which one is captured by TS typesystem here
function assertGuard<T>(v: T | undefined) {
if (v === undefined || v === null || v === '' || v === 0 || v === false) {
throw new AssertionError({message: 'foo'})
}
}
This one compiles, but call to assertGuard(foo) doesn't recognize that for undefined it will return never so doesn't restrict foo to Foo.
I've found possible workarounds but i consider classical assert a cleaner approach:
function assertResultDefined<T>(v: T|undefined): T | never {
if (v === undefined) {
throw new Error('foo');
}
return v;
}
function die(): never { throw new Error('value expected)}
const foo = assertResultDefined(getFoo()) // foo is Foo, undefined is erased
const foo = getFoo() || die();
// undefined is erased from foo
/ CONS: doesn't play well with types that interpolate to `false` like 0, ''