Typescript support for variants

Currently variant in typescript looks like this:

type MyVariant = { "ok" : string } | { "err" : string  }

It is hard to use type predicates.

I think it would be great to have following:

type Type_1 = { "ok" : string }
type Type_2 = { "err" : string }
type MyVariant = Type_1 | Type_2

Now we can use type predicates:

function isType1(value: Type_1 | Type_2): value is Type_1 {
    return (value as Type_1).hasOwnProperty("ok");
}

function isType2(value: Type_1 | Type_2): value is Type_2 {
    return (value as Type_2).hasOwnProperty("err");
}

What gives us:

function myFunc(a: MyVariant) {
    if (isType1(a)) {
        let err = a.err;//TS2339: Property 'err' does not exist on type 'Type_1'.
        let ok = a.ok;
    } else if (isType2(a)) {
        let err = a.err;
        let ok = a.ok;//TS2339: Property 'ok' does not exist on type 'Type_2'.
    }
}

Profit!

@kpeacock Please tag someone who can help

Thanks!

2 Likes

@chenyan @claudio we’ve discussed this before internally, but here’s some community feedback

1 Like

@kpeacock, are there any improvements in the roadmap?
Thanks in advance

I don’t know typescript well, but does the following work?

function myFunc(a: MyVariant) {
  if (a.hasOwnProperty("ok")) {
    let ok = a.ok;
  } else if (a.hasOwnProperty("err")) {
    let err = a.err;
  }
}

For TS generation, wouldn’t it be better to generate variants like this:

instead of:
type MyVariant = { "ok" : string } | { "err" : string  }

this:
type MyVariant = { type: "ok"; val: string} | { type: "err"; val: string }

because this is the way ts defines discriminated unions and you can write code like:
if (c1.type === "ok") {
    //compiler will narrow the type to "ok", exhaustive switch blocks are also possible
    const c2 = c1;
  }

Alternatively I suggest to try out ts-pattern - npm , then it should be possible to do pattern matching on the current implementation. You can write code like (didn’t tested, but it looks like compiler doesn’t complain):

  const a1: Result = a();

  const output = match(a1)
    .with({ Ok: __ }, (myOk) => {
      //type is narrowed
      const ok = myOk.Ok;
    })
    .with({ Err: __ }, (myErr) => {
      const err2 = myErr.Err;
      match(err2)
        .with({ RegistrationFailed: __ }, (p) => "{}")
        .with({ AccountAlreadyRegistered: __ }, (p2) => "{}")
        .exhaustive();
    })
    .exhaustive();
1 Like

@shalexbas Thank you so much for provided approach!

1 Like