In TypeScript, is there a type for truthy?
I have this method:
Object.keys(lck.lockholders).length;
enqueue(k: any, obj?: any): void
I think with TS there is a way to check for empty strings ''
, by the way.
And I want to convert it to:
enqueue(k: Truthy, obj?: any): void
except I don't know how to define the type for Truthy.
The reason I want this is that I don't want users to pass in null
, undefined
, ''
, etc, as the key to a hash.
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
(see microsoft/TypeScript#15135).
Even if you have Falsy
as above, there are no negated types (see microsoft/TypeScript#4196) 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!