-
-
Notifications
You must be signed in to change notification settings - Fork 113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Typing errors with the isEmptyStringOrWhitespace type guard #206
Comments
One possibility would be to redefine function isWhitespaceString(value: unknown): value is " " {
return isString(value) && /^\s+$/.test(value);
}
function isEmptyStringOrWhitespace(value: unknown): value is "" | " " {
return isEmptyString(value) || isWhitespaceString(value);
} The behavior would be more accurate, but still not 100% correct as a string could contain any number of whitespaces. However, I don't think that it's possible to express that in TS. |
We can actually type it correctly: https://github.com/sindresorhus/type-fest/blob/0f732371f607fe44e934d178eb97ad71eccda873/source/internal.d.ts#L315-L322 But I don't think that would be very useful. I think the solution above is the most practical one. |
@marlun78 it is not perfect indeed, but it is a better solution than the current situation. @sindresorhus there is another solution, more appropriate in my opinion, which consists of using the "opaque types" concept. You must create a simple type that "simulates" the complex type you are interested in, and create a type guard function using it. export const BRAND: unique symbol = Symbol("zod_brand");
export type BRAND<T extends string | number | symbol> = {
[BRAND]: { [k in T]: true };
}; Then you create the type for the empty or whitespaces (\t\n\t\r\v) strings, by intersecting the global type (string here) with the BRAND type adding the virtual definition of the subset of the global type (here it means the empty or whitespaces string subset): export type EmptyOrWhitespacesString = string & BRAND<"EmptyOrWhitespacesString">; Then you modify the declaration of your isEmptyStringOrWhitespace type guard function (same for your assertEmptyStringOrWhitespace assertion guard function) to use this type: function isEmptyStringOrWhitespace(value: unknown): value is EmptyOrWhitespacesString Thus, you have a type matching perfectly the empty or whitespaces strings, even if it is "virtually", but it is enough to manage the typing correctly in every cases. Example of use: let value: ATYPE = <any value>; // ATYPE can be any type (string, unknown, Object, any, number | boolean | null, etc...)
if (is.emptyStringOrWhitespace(value)) {
value; // => is typed by EmptyOrWhitespacesString and allows to use value as a string because this is the global type we use to define the EmptyOrWhitespacesString type (value.length is allowed for example)
}
else {
value; // => is typed by ATYPE
}
value; // => is typed by ATYPE and this is exactly the behavior we wanted. ;) Regards. |
=/ ... |
@vtgn Sorry, I missed your last comment here. Are there any benefits of the "brand" over what's in 25a3768? The benefit of One hybrid approach would be to use: export type EmptyOrWhitespacesString = '' | (string & BRAND<"WhitespacesString">);
export function isEmptyStringOrWhitespace(value: unknown): value is EmptyOrWhitespacesString { |
Hi!
There is a problem similar to the issue #176 with the isEmptyStringOrWhitespace type guard:
Indeed, this type guard is incorrect because the type is not enough precise and it leads to typing problems when it returns false. Example:
The workaround is to use a type assertion with the as keyword for the cases where the compiler see values as non string whereas they are. :/
I don't know if it is possible to create a precise type for empty or whitespaces strings. I will try and tell you if I find.
Regards.
The text was updated successfully, but these errors were encountered: