Is there a way for TypeScript to statically check for an empty string? Is there a way to statically require a non-empty string to be passed to a function?
let fn = function(a:string){
};
fn('');
or
let a = '';
fn(a);
Can TS help us here?
I believe this is as close as you're going to get only using the typing system (rather than having a 'nonEmptyString' class)
type nonEmptyString = never; // Cannot be implicitly cast to
function isNonEmptyString(str: string): str is nonEmptyString {
return str && str.length > 0; // Or any other logic, removing whitespace, etc.
}
Testing it:
let fn = function(a: nonEmptyString) {
}
let someStr = '';
if (isNonEmptyString(someStr)) {
fn(someStr); // Valid
} else {
fn(someStr); // Compile error
}
Unfortunately, you end up with warts since nonEmptyString is never. Which means you need to explicitly cast nonEmptyString back to string.
let fn = function(a: nonEmptyString) {
let len = a.length; // Invalid
let len2 = (<string>a).length; // Valid
let str = a + 'something else'; // Valid (str is now typed as string)
}
One possible resolution is:
type nonEmptyString = string & { __nonEmptyStr: never };
Which alleviates the problem of having to explicitly cast back to a string (all three tests above are valid), but does pollute the type with __nonEmptyStr (which will be undefined if referenced).
string type then: const isNotBlank = (str?: string|null): str is string => (str !== undefined && str !== null && str.trim().length > 0)? Genuine question - what would be wrong with this approach?.You can use this trick in your specific case:
function fn<T extends string>(a: T extends '' ? never : T) {
// But TypeScript won't know here that !!a === true
}
fn(''); // Error
fn('foo'); // No error
Note that this does NOT work when passing a variable that has type string:
const myEmptyString: string = '';
fn(myEmptyString); // Should error, but does not
type NonEmptyString<T> = T extends '' ? never : TYou could maybe type it with overloads such as to give a bad return type on "" that will make things error as soon as you use it elsewhere:
type MyFnType = {
(a: "") => never;
(a: string) => whatever;
}
function fn: MyFnType = ...
function fn(s: ''): never; function fn(s: string): string; function fn(s: string | '') {/* do the real work here *}Workaround
type NonEmpty<T extends string = string> = T extends '' ? never : T
// or extends NonNullable
type NonNull<T> = T extends string ? NonEmpty<T> : NonNullable<T>
Note! It only works with Literal Types and Generic Type, not works with Primitives Type string.
NonNull as Valuable.const foo: NonEmpty = '' will result in foo being of type stringUsing template literal types:
type NotEmptyString = `${any}${string}`;
This is something that just works this way. This works in TS code but you must check values coming from external apis.
Discussion related to ${string}${string} problems is here: https://github.com/microsoft/TypeScript/issues/57233
You can use !a to check if a is an empty string.