effect icon indicating copy to clipboard operation
effect copied to clipboard

Add `optionFromEmptyString`

Open f15u opened this issue 2 years ago • 4 comments

🚀 Feature request

Current Behavior

At the moment I did define the following schema to handle empty strings as Option.

const optionFromEmptyString = pipe(
  Schema.string,
  Schema.transform(
    Schema.optionFromSelf(Schema.string),
    (s) => s.length > 0 ? Option.some(s) : Option.none(),
    (s) => Option.getOrElse(s, () => '')
  )
)

Desired Behavior

Having the schema implemented in @effect/schema

Suggested Solution

const optionFromEmptyString = pipe(
  Schema.string,
  Schema.transform(
    Schema.optionFromSelf(Schema.string),
    (s) => s.length > 0 ? Option.some(s) : Option.none(),
    (s) => Option.getOrElse(s, () => '')
  )
)

Who does this impact? Who is this for?

I think everyone who uses Schema with front-end forms or where null cannot be used. Eg: forms returns empty strings when are submitted.

Describe alternatives you've considered

Declare the schema at app level.

Additional context

Your environment

Software Version(s)
@effect/schema 0.15.1
TypeScript 5.1.6

f15u avatar Jul 18 '23 11:07 f15u

@FedericoBiccheddu string is already a disjoint union with respect to non empty strings: string = "" + NonEmptyString, what's the benefit of transforming into Option<NonEmptyString>?

gcanti avatar Jul 27 '23 06:07 gcanti

Working with data coming from a form and parsing it at the edge, simplify handling the parsed data a lot when working with Option at domain level when values are missing. For example, an empty or missing value coming from the front-end it will be always an empty string (eg: input, select, etc).

https://codesandbox.io/p/sandbox/festive-brook-9v3vgj?file=%2Fsrc%2Froutes%2F%2Bpage.server.ts%3A35%2C1

I created this sandbox to showcase what I think is a very common use-case and try to answer your question.

f15u avatar Jul 27 '23 11:07 f15u

The sandbox shows how optionFromEmptyString works, however I still don't get:

  1. in the transformation why the To schema is Schema.optionFromSelf(Schema.string) instead of Schema.optionFromSelf(Schema.string.pipe(Schema.nonEmpty()))? Is it intended?
  2. assuming To = Schema.optionFromSelf(Schema.string.pipe(Schema.nonEmpty())), how transforming a string into an equivalent type (Option<NonEmptyString>) makes easier to handle the parsed data?

gcanti avatar Jul 27 '23 12:07 gcanti

  1. Didn't know I can do that. Thank you for pointing it out; I did update my sandbox
  2. Without transforming in an Option, every time I want to check if the value is present or not, I should check if it equals to an empty string instead of using a semantic type like Option.

For example, if we have form when we can choose via a select if we want to use a template before creating something and invite a team member via email, when the template field is empty and/or the field teamInvite is empty, then the form submit "" as value, so in the back we can do something like:

// snip
    Effect.gen(function* ($) {
      const formData = yield* $(Effect.tryPromise(() => request.formData()))

      const payload = yield* $(Schema.parse(payloadS)(Object.fromEntries(formData.entries())))

      if (Option.isSome(payload.template)) {
        // Apply the logic to use a template
      }

      if (Option.isSome(payload.teamInvite)) {
        // Apply the logic to invite the team, check if there are already members with the email and so on
      }

      return yield* $(Schema.encode(payloadS)(payload))
    }),
// snip

Making this specific example, It made me think that this combinator could receive another schema and the general use case I think still applies.

f15u avatar Jul 27 '23 13:07 f15u

Closing in favour of https://github.com/Effect-TS/effect/issues/3335

@FedericoBiccheddu you may want to join the discussion here https://discordapp.com/channels/795981131316985866/1265704338257481778/1265949127334826048

gcanti avatar Jul 25 '24 09:07 gcanti