variant icon indicating copy to clipboard operation
variant copied to clipboard

Generic variant usage with function as payload

Open ohana54 opened this issue 3 years ago • 3 comments

Hi again :)

I'm trying to create a generic variant:

const [Matcher, __Matcher] = genericVariant(({ T }) => ({
  Specific: payload(T),
  Custom: (payload: (v: typeof T) => boolean) => ({ payload }),
}))
type Matcher<T, TType extends GTypeNames<typeof __Matcher> = undefined> = GVariantOf<typeof __Matcher, TType, { T: T }>

Specific is pretty straight-forward, where Custom accepts a function that needs to use the generic type.

When using Specific in a "wrong" way, I get an expected error:

// error due to `number` payload not matching `string`
const matcher: Matcher<string> = Matcher.Specific(5)

When using Custom in a "wrong" way, I don't get an error:

const matcher: Matcher<string> = Matcher.Custom((v: number) => v === 1)

I'm probably doing something wrong with the variant creation, how can I get the generic type of Custom to work?

Thanks!

ohana54 avatar Feb 21 '21 14:02 ohana54

Hey I haven't looked into this yet but it's more likely it's my fault than yours. Generic variants are... hard. I probably overlooked a case. I'll see what I can do.

paarthenon avatar Feb 21 '21 17:02 paarthenon

Hello @ohana54. I've finally found freedom from professional obligations (say that five times fast). I've been taking some creative license and rewriting most of the library for variant 3.0. As part of that, I tackled generics. There's a new interface I hope you'll find satisfactory. I've ported your example to use it.

As a brief reminder, Variant 3.0 changes the purpose of the variant() function to be a catch-all creation tool. The function to create a single case is now called variation(). You can see the full list of changes in Issue #17 .

const Matcher = variant(onTerms(({T}) => ({
    Specific: payload(T),
    Custom: (payload: (v: typeof T) => boolean) => ({payload}),
})))
type Matcher<T, TType extends TypeNames<typeof Matcher> = undefined> = GVariantOf<typeof Matcher, TType, {T: T}>;

Note you no longer need a __Matcher variable.

With these changes (and executing on TS 4.2.4) I receive an error when using Custom in a "wrong" way.

const matcher: Matcher<string> = Matcher.Custom((v: number) => v === 1);

Full error:

Type '{ type: "Custom"; payload: (v: number) => boolean; }' is not assignable to type '{ type: "Specific"; payload: string; } | { type: "Custom"; payload: (v: string) => boolean; }'.
  Type '{ type: "Custom"; payload: (v: number) => boolean; }' is not assignable to type '{ type: "Custom"; payload: (v: string) => boolean; }'.
    Types of property 'payload' are incompatible.
      Type '(v: number) => boolean' is not assignable to type '(v: string) => boolean'.
        Types of parameters 'v' and 'v' are incompatible.
          Type 'string' is not assignable to type 'number'.ts(2322)

I've added more details of the new generics implementation in this comment.

paarthenon avatar May 02 '21 19:05 paarthenon

Hi, sounds great!! I'll track the progress and wait for an alpha/beta version to try.

ohana54 avatar May 06 '21 17:05 ohana54