ts-pattern icon indicating copy to clipboard operation
ts-pattern copied to clipboard

Pattern match on an `Record<>`'s key.

Open guyzmo opened this issue 4 years ago • 3 comments

Is your feature request related to a problem? Please describe.

I have an object which type is alike the following:

  • const type MyErrorType = Record<string, {message: string}>

the record's key is the name of a form element, which is typed like an enum, e.g. for a form with inputs "firstname, lastname, email", the type would be alike:

  • const type MyErrorType = Record<"firstname"|"lastname"|"email", {message: string}>

For a real world use case, it's the errors object returned by "react-hook-form", for which I wish to use pattern matching.

Today I can pattern match on the keys I know how to deal with, e.g.:

    match(errors) // errors: MyErrorType
      .with(
        {firstname: {message: select()}},
        {lastname: {message: select()}}, (res) => console.error(`Name error: ${res}`))

But I have no elegant solution to match the keys I'm not sure how to deal with.

Describe the solution you'd like

Something I believe would be elegant, would be to have a pattern match destructuring on the object's key, maybe something looking like:

      .with({[select("key")]: {message: select("message")}}, ({key,message}) => console.error(`${key}: ${message}`)})

Describe alternatives you've considered

Today the only solution I found is:

      .with(__, (res) => {const keys = Object.keys(res); console.error(`${keys[0]}: ${res[keys[0]]?.message ?? "is invalid"}`)})

Additional context

I have RTFM the manual, looked through the issues, but I might have missed something better than my alternative. I also did try my solution, and it was skipped straight to the otherwise().

About the use case, I'm trying to pattern match within react-hook-form's handleSubmit second argument (that contains the form errors), which type is DeepMap<T, FieldErrors> where T is the type of the form, and FieldErrors a structure containing information about an error for a form field.

P.S.: Thank you for this work, this is something I missed a lot when doing frontend dev 😉

guyzmo avatar Mar 25 '21 17:03 guyzmo

@gvergnaud You closed this, but this is one of the feature I don't find in ts-pattern. How can I match a Record with string key and a pattern as a value?

Ideally, with something like P.map() (with P.string, P.number or P.symbol, or a union of them allowed as key):

match(value)
  .with(P.record(P.string, { foo: P.number }), (value) => { /* value is Record<string, { foo: number }> */ })
  .otherwise(() => {});

zoontek avatar Jul 24 '24 08:07 zoontek

Yeah I do think we should implement this. Happy to accept contributions to add a P.record that behaves like P.map

There's also a branch that adds a P.object pattern (mimicking what the object type matches) which double down as a namespace. I'm wondering if we should add record under P.object.record to avoid clashing with the upcoming Record & Tuple proposal.

gvergnaud avatar Jul 30 '24 22:07 gvergnaud

I agree with P.object.record instead of P.record. I will try to see what I can do.

zoontek avatar Jul 31 '24 15:07 zoontek