-
-
Notifications
You must be signed in to change notification settings - Fork 217
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
Optional keys respecting exactOptionalPropertyTypes #385
Comments
Thank you for creating this issue. Do you have any ideas how we could improve our API to support this? One option is to have two different schema function for both use cases and another to stick with |
Both sounds good to me. |
What would you name the function or configuration option? Feel free to provide sample code for your dream API. Maybe I can make it happen. |
I first thought just adding a simple boolean flag to the optional function: const schema = object({
key: optional(string(), true),
}); But that would be weird when the schema is just some string with validations: const schema = optional(string([email(), minLength(8)]), true)); I guess a new function that only makes sense for objects makes the most sense. The name partial was choosen because of the const schema = object({
key: partial(string()),
}); Other synonyms could be:
|
Thanks for your feedback. I will think about it, but do not expect a quick response as my focus is currently on other areas of the library. |
This issue is one of the main reasons I stopped using zod. If valibot could get it done that would be really neat. |
Can you explain why this feature is important to you? What are the problems with the current approach? |
@fabian-hiller Sure. In my case I have an existing external type like this: type Maintainer = { name?: string, url?: string, email?: string } I also have I want to accurately represent this type using a schema. But currently that is not possible (as I understand it) because using type Maintainer = { name?: string | undefined, url?: string | undefined, email?: string | undefined } This will cause issues in two places. First I want to enforce that my schema matches the TS type and so use const maintainerSchema = z.object({ ... }) satisfies v.BaseSchema<Maintainer, Maintainer, v.BaseIssue<unknown>> This does not work because the schema is not assignable to the TS type. The issue is the explicit The same issue will also prevent me from assigning objects of type Hope this makes it clear :) |
Thats exactly the issue I have. At the moment I solved it by manually type-checking the validated valibot object that no undefined value is used when the property exists. But for some complex objects thats hundreds of lines of code to check dozens of properties. |
Here is the workaround code I now use in type RemoveExplicitUndefined<T> = T extends undefined
? never
: T extends z.BRAND<string>
? T
: T extends object
? { [K in keyof T]: RemoveExplicitUndefined<T[K]> }
: T;
export function removeExplicitUndefined(value: undefined): never;
export function removeExplicitUndefined<T>(
value: T
): RemoveExplicitUndefined<T>;
/**
* Recursively removes all instances of explicit undefined from a value.
* This mostly gets rid of explicit undefined properties in objects.
* @param value The value.
* @throws Error if the passed value is undefined.
*/
export function removeExplicitUndefined(value: unknown) {
if (value === undefined) {
throw new Error("Cannot remove undefined from undefined!");
}
if (value === null || typeof value !== "object") {
return value;
}
return Object.fromEntries(
Object.entries(value)
.filter(([, v]) => v !== undefined)
.map(([key, v]) => [key, removeExplicitUndefined(v)])
);
}
// Usage
const tsValidValue = removeExplicitUndefined(schemaValidValue); |
Thanks for all the feedback. According to Colin (maintainer of Zod) it is possible to detect if |
|
Another name that came to mind is |
Maybe is perfect. Because the value may maybe there - and its the exact definition of the value that may be there. |
There is one more problem that came to my mind. The const OptionalSchema = v.optional(v.string()) // string | undefined
const MaybeSchema = v.maybe(v.string()) // string <--
const ObjectSchema = v.object({
foo: OptionalSchema,
bar: MaybeSchema
}); // { foo?: string | undefined; bar?: string } |
May an API like this: v.maybe(v.object({
foo: OptionalSchema,
bar: MaybeSchema
}, ['bar']); But yes. It looks horrible compared to the clean and easy API currently. Is it a big problem if a |
A fix could be to add |
How would this API solve the problem?
Not sure. The problem is that each schema is independent and should not produce unexpected behavior that can lead to runtime errors when using it without a specific schema such as |
It would work similar to
When |
Can you explain the |
// should be recognized on the type-level like: type Maybe<string> = string
const myStr = v.maybe(v.string()); // Maybe<string>
v.object(
str: myStr,
); // object { str?: string } I don't know whether this works. I am struggling with those advanced TS types all the time. My Idea was to make |
This is what I was talking about in this comment. It can work, but there are edge cases. Unwrapping is one solution. The problem here is that we can't know how deeply |
As written here, I am toying with the idea of simplifying the question mark handling. This would also allow us to fix this issue. |
I have been trying to implement and support |
same here. need to match exact optionals. |
Good news! |
v0.39.0 is available |
Is there a schema like |
Not yet, |
I don't mind the name, just want something short for |
I am unsure about |
I think |
I may introduce an |
I've tested the new release and it works as expected. Thanks ❤️ I started with the smallest module and was able to remove a lot of logic as I can now return the validation result directly and don't have to check for those |
@alecmev should |
I can’t comment on what makes sense for the API overall, but I personally just want sugar for |
The validation of |
Yep, I use |
Thank you very much for your feedback! I will think about it and probably get back to you tomorrow or the day after. |
|
Great, thank you 👍 |
v0.40.0 is available 🚀 |
@fabian-hiller thanks for all the work. great job. |
The runtime behavior of |
I answered this in the linked issue. |
TypeScript 4.4 added exactOptionalPropertyTypes as a more strict setting to better handle optional object keys. This is currently not supported in valibot.
It would be great if there is a way to define optional attributes that do not automatically add undefined to the type. zod has the same problem (colinhacks/zod#635, colinhacks/zod#1510), while io-ts works correctly but is hard to use.
The text was updated successfully, but these errors were encountered: