Skip to content
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

Reduce boilerplate for custom actions #614

Closed
IlyaSemenov opened this issue May 30, 2024 · 4 comments
Closed

Reduce boilerplate for custom actions #614

IlyaSemenov opened this issue May 30, 2024 · 4 comments
Assignees
Labels
enhancement New feature or request

Comments

@IlyaSemenov
Copy link
Contributor

Before the new pipe API, I was creating ad-hoc validators with something like:

export function nonEmpty<TInput extends string | any[]>(error?: ErrorMessage) {
  return minLength<TInput, 1>(1, error)
}

after the update, the least I could come up with was:

// Not exported by valibot, need to redefine this.
type LengthInput = string | unknown[]

export function nonEmpty<TInput extends LengthInput, const TMessage extends
  | ErrorMessage<MinLengthIssue<TInput, 1>>
  | undefined>(message?: TMessage): MinLengthAction<TInput, 1, TMessage>

export function nonEmpty(message = "Non-empty value required.") {
  return minLength(1, message)
}

If I remove the function definition boilerplate and only keep the last 3 lines, it works but it's not typed properly:

image

My question is, can we come up with some kind of approved recipe and/or reduced boilerplate for trivial library extensions such as above? In my perfect world, it should be possible to come up with multitude of similar one-liners, not having to toss unreadable mess of copy/pasted generics.

@fabian-hiller
Copy link
Owner

Thanks for the feedback. I will check if I want to export these types, but in general it is probably a good idea. nonEmpty is now natively supported out of the box, so you don't have to add it yourself.

In general, there is no difference from before. The only difference is that the API has become more type-safe, which requires more precious typing, resulting in more TypeScript code. But if you don't care, you can write it much the same way as before. Here is an example. You can test it in this playground.

import * as v from 'valibot';

type LengthInput = string | unknown[];

function nonEmpty<TInput extends LengthInput>(
  message: string = 'Non-empty value required.'
): v.MinLengthAction<TInput, 1, string> {
  return v.minLength(1, message);
}

If you prefer precise types, I have rewritten your code to make it a bit simpler. You can test it in this playground.

import * as v from 'valibot';

type LengthInput = string | unknown[];

function nonEmpty<
  TInput extends LengthInput,
  const TMessage extends v.ErrorMessage<v.MinLengthIssue<TInput, 1>>,
>(
  message: TMessage = 'Non-empty value required.' as TMessage
): v.MinLengthAction<TInput, 1, TMessage> {
  return v.minLength(1, message);
}

@fabian-hiller fabian-hiller self-assigned this May 30, 2024
@fabian-hiller fabian-hiller added the enhancement New feature or request label May 30, 2024
@IlyaSemenov
Copy link
Contributor Author

Another use case is:

export function integerNumber<TMessage extends v.ErrorMessage<v.NumberIssue>>(message?: TMessage) {
  return v.pipe(v.number(message), v.integer())
}

is there a better way to type this? Most of the schemas use function overload to distinguish between the message and undefined:

declare function number(): NumberSchema<undefined>;
declare function number<const TMessage extends ErrorMessage<NumberIssue> | undefined>(message: TMessage): NumberSchema<TMessage>;

Is there a way to proxy this contract somehow to the extending one-liners?

@fabian-hiller
Copy link
Owner

As I understand it, this is not possible without drawbacks. That's why I use overload signatures.

@fabian-hiller
Copy link
Owner

I added ContentInput, ContentRequirement, LengthInput, SizeInput and ValueInput to the exports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants