I have a specific pattern to check whether a value is present or not after a user submission.
Since I don't want to repeat this logic multiple times, I extracted it into a valIsBlank
method so the logic is centralized. The problem is that the TypeScript compiler does not infer the presence (or not) of the field correctly anymore.
Here is some sample code to illustrate the issue :
type Value<T> = T | null | undefined;
function getOr<T>(value: Value<T>, fallback: T): T {
// With this : the code works as expected with no tsc errors ✅
// const isBlank = value === undefined || value === null || (typeof value === 'string' && value.trim().length === 0);
// With this : the line 'return value;' triggers an error ❌
const isBlank = valIsBlank(value);
// With this : the line 'return value;' triggers an error ❌
// const isBlank = ((value: Value<T>) => value === undefined || value === null || (typeof value === 'string' && value.trim().length === 0))(value);
if (isBlank) {
return fallback;
}
return value;
}
function valIsBlank<T>(value: Value<T>): boolean {
return value === undefined || value === null || (typeof value === 'string' && value.trim().length === 0);
}
Is there a way for the compiler to understand what's happening in isBlank
and infer that if it returns false
, then the type of value
becomes simply T
? Like it does when the check is inlined directly in the function.
Naturally, I'd like to avoid casting the value with as T
or !
.
CodePudding user response:
In order for the compiler to understand that the output of a boolean
-returning function like valIsBlank()
acts as a type guard on its input, you need to annotate the return type with a type predicate.
If valIsBlank(value)
returns true
then you know that value
is undefined
or null
or a "blank string" (which seems to be a string consisting only of spaces). So you could write it this way:
declare function valIsBlank<T>(value: Value<T>):
value is null | undefined | (T & BlankString);
There isn't actually a specific type in TypeScript corresponding to a blank string; for that you'd need something like regular expression validated string types as discussed in microsoft/TypeScript#41160, which aren't part of the language. I'm not going to worry much about it because it seems to be beside the point of the question. For now I'll just define it as a union of a few likely string literal types and move on:
type BlankString = "" | " " | " " | " " //