I want to make a function that will try running provided function, and will throw unless it returns a truthy value, like this:
function test<T>(runnable: () => T): T {
const value = runnable();
if (! value) throw new Error(`Only truthy`);
return value;
}
const a = test(() => {
return (Math.random() < 0.5) ? undefined : "ok";
});
console.log(a);
How do I change the definition of test
so that TS knows that a
is always a string, not string|undefined
?
CodePudding user response:
If you make the falsy types part of inner function return type, but not part of the outer function type, then this is possible:
First, make a Falsy
type alias:
type Falsy = false | null | undefined | 0 | ''
And now make a union with T
for the inner function return type:
function test<T>(runnable: () => T | Falsy): T {
const value = runnable();
if (!value) throw new Error(`Only truthy`);
return value;
}
This means that T
is inferred to not include the Falsy
types. And the inner runnable
function is allowed to return T
or any Falsy
type. Then you check for the Falsy
type and proceed to the return only if the value is not a Falsy
, which must be a T
.
Now this function has a return type of "ok"
since the error traps all other results.
const a = test(() => {
return (Math.random() < 0.5) ? undefined : "ok";
});
a // type: "ok"