-
Notifications
You must be signed in to change notification settings - Fork 10
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
RFE: Better extensibility #73
Comments
Glad to hear that Valita has been of use. It's very interesting to hear about your use-case. The original purpose of The lightest approach would be to have a minimal selection of public properties that's just enough to write custom type-safe visitors: function visit(type: Type | Optional) {
switch (type.name) {
case "literal":
return type.value;
case "options":
return type.options.map(visit);
// and so on ...
}
} Some of them are already public, like Regarding customized error messages and union reporting: We recommend using a similar visitor approach, e.g. a function that branches based on Combining that with the visitor suggestion allows writing e.g. a custom helper that finds the issue with the deepest path, and also works nicely with nested unions: function* flattenIssues(
issues: Issue[],
path: (string | number)[] = [],
): Iterable<Issue> {
for (const issue of issues) {
if (issue.code === "invalid_union") {
yield* flattenIssues(issue.issues, [...path, ...issue.path]);
} else {
yield { ...issue, path: [...path, ...issue.path] };
}
}
}
function findDeepestIssue(issues: readonly Issue[]): Issue | undefined {
let deepest: Issue | undefined;
for (const issue of flattenIssues(issues)) {
if (!deepest || deepest.path.length < issue.path.length) {
deepest = issue;
}
}
return deepest;
} |
That is useful. I will see if I can find time to a PR that exposes enough for us to transition to .4 |
One more issue popped up this week. We are creating a deepPartial implementation/**
* Similar to `ObjectType.partial()` except it recurses into nested objects.
* Rest types are not supported.
*/
export function deepPartial<Shape extends ObjectShape>(
s: v.ObjectType<Shape, undefined>,
) {
const shape = {} as Record<string, unknown>;
for (const [key, type] of Object.entries(s.shape)) {
if (type.name === 'object') {
shape[key] = deepPartial(type as v.ObjectType).optional();
} else {
shape[key] = type.optional();
}
}
return v.object(shape as {[K in keyof Shape]: v.Optional<v.Infer<Shape[K]>>});
} |
We are building some features on top of Valita and with the .4 release a lot of the public APIs that we depend on are now marked as private. I'm going to outline the things we are using to see if we can figure out how to expose these in a clean way.
Command Line Parser
We have a library that takes a Valita schema and generates a command line parser.
The code is ~500 LOC and it will be open sourced this year. I can give access to it early if you are curious.
API Used
toTerminals
: This is used for the visitor pattern. We use it to gather the literals and the types.ArrayType.prefix
,ArrayType.rest
,ArrayType.suffix
:toTerminals
was not called on these so we had to do that manually. This is used with the above. I believe we can mostly create our own visitor except for these which are not exposed.func
: We used this to get the default value out of an optional. I think I can test forname === 'optiopnal'
and in that case there is no default value. Otherwise I can callschema.parse(undefined)
and see what I get out of it.Customized Error Messages
No breaking changes here but it does reproduce some of the functionality (~100 LOC). Not a big deal at the moment.
Better Union Error Reporting
When a union fails to parse we find the longest partial match and show the error for that. This allows errors like
API Used
UnionType.options
: This is used to get the different types of the union and wetry
all of them to see which one gives use the longest path.Sample Implementation
The text was updated successfully, but these errors were encountered: