In TypeScript, is there a type for truthy?
I have this method: Object.keys(lck.lockholders).length; enqueue(k: any, obj?: any): void think with TS there is a
I'm not sure why you need this but it's interesting. In all honesty the short answer is: TypeScript isn't geared for this and you'd probably be better off doing runtime checks and documenting your code so that developers are aware that the k
param should be truthy. Still, if you're set on trying to force TypeScript to do something like this, read on:
Note: for the below to work, turn on the strictNullChecks
compiler option. It's kind of necessary, since being unable to distinguish Truthy
from Truthy | null | undefined
would be a problem.
You can almost define falsy, which is like
type Falsy = false | 0 | "" | null | undefined
except NaN
is also falsy, and TypeScript doesn't have a numeric literal for NaN
.
Even if you have Falsy
as above, there are no negated types in TypeScript, so there's no way to express Truthy
as "everything but Falsy
".
You could try to use use conditional types to exclude possibly-falsy parameters in enqueue()
, but it is weird:
type DefinitelyTruthy<T> =
false extends T ? never :
0 extends T ? never :
"" extends T ? never :
null extends T ? never :
undefined extends T ? never :
T
declare function enqueue<T extends number | string | true | object>(
k: T & DefinitelyTruthy<T>,
obj?: any
): void
declare const str: string;
enqueue(str); // error, might be falsy
enqueue("a"); // okay
enqueue(1); // okay
enqueue(0); // error
enqueue(NaN); // error
enqueue(true); // okay
enqueue(false); // error
enqueue([]); //okay
enqueue({a: "hello"}); // okay
enqueue({}); // error, interpreted as type {} which could be an empty string:
const zilch = "" as {};
enqueue(zilch); // error, see?
Note how it won't allow anything which it thinks might be falsy, which is possibly what you are trying to achieve. Can't tell.
I see you edited the question to clarify that the k
parameter should really be a string
(or possibly a symbol
) and that the only value you need to exclude is the empty string ""
. In that case you could simplify the above to:
type DefinitelyNotEmptyString<T> = "" extends T ? never : T
declare function enqueue<T extends string | symbol>(
k: T & DefinitelyNotEmptyString<T>,
obj?: any
): void
enqueue(""); // error
enqueue("a"); // okay
All of that is great, but unfortunately there's the problem that if you pass a general string
to enqueue()
it will fail, and sometimes a developer might need to do that if the value they are using for the k
parameter isn't a string literal they have specified:
declare const str: string; // comes from somewhere else
enqueue(str); // error! how do I do this?
To deal with this, you can try to create a nominal type which you can use to identify to the compiler that a value has been checked for emptiness, and then make a user-defined type guard to constrain a string
to that type:
type NotEmptyString = string & {"***NotEmptyString***": true};
function notEmptyString(x: string): x is NotEmptyString {
return x !== "";
}
Now the developer can do this:
declare const str: string;
enqueue(str); // error, might be falsy
if (notEmptyString(str)) {
enqueue(str); // okay, str is NotEmptyString
}
Whew! That's a lot of hoop jumping. It's up to you if you think this is worth it. Okay, hope that helps. Good luck!
There is no Truthy
type, BUT you can leverage type guard system (type predicate) to help TypeScript to understand what really is truthy
and assign it truthy
type!
Let's define Falsy
type and generic isTruthy
function:
type Falsy = false | 0 | '' | null | undefined;
// this is a type predicate - if x is `truthy`, then it's T
const isTruthy = <T>(x: T | Falsy): x is T => !!x;
Now we can use out isTruthy
function to find truthy
values and TypeScript will correctly assign the "truthy"
type to the result.
Examples:
{ // removing boolean "false" type
const result: string | false = Math.random() > 0.5 ? 'a' : false;
const truthyResult: string = isTruthy(result) ? result : 'was false';
}
{ // filtering only numbers from array
const result: (number | undefined)[] = [42, undefined, 8, 16];
const truthyResult: number[] = result.filter(isTruthy);
}
{ // filtering only Promises from array
const result: (Promise<string> | null)[] = [Promise.resolve('a'), null, Promise.resolve('b'), Promise.resolve('c')];
const truthyResult: Promise<string>[] = result.filter(isTruthy);
}