Create your own enum

Here is how to create your own enum.

Define Opt<T> enum variants using type

At first, you need to define variant and containing value using type. Each variant is represented by [variant-name]: [containing-value-type].

type OptVariants<T> {
  Some: T;
  None: null;
};

Create enum class using Enum base class

Next, create the enum class using Enum base class.

import { Enum } from "tnum/core"; 

type OptVariants<T> {
  Some: T;
  None: null;
};

export class Opt<T> extends Enum<OptVariants<T>> {} 

To create enum instance:

const opt0 = new Opt("Some", 10); // opt0 is Opt<number>
const opt1 = new Opt<unknown>("None", null); // opt1 is Opt<unknown>

Create Some(T) and None() static methods

To generate Opt<T> with Some(T) or None() as in Rust:

// ...

export class Opt<T> extends Enum<OptVariants<T>> {
  static Some<T>(value: T): Opt<T> {
    return new Opt("Some", value);
  }

  static None<T>(): Opt<T> {
    return new Opt<T>("None", null);
  }
}

export const Some = Opt.Some;
export const None = Opt.None;

To create enum instance:

const opt0 = Some(10); // opt0 is Opt<number>
const opt1 = None(); // opt1 is Opt<unknown>

Add type-guard methods

To add isOk() and isNone() type-guard methods using EnumNarrowed:

import { Enum, EnumNarrowed } from "tnum/core";

// ...

export class Opt<T> extends Enum<OptVariants<T>> {
  // ...

  isSome(): this is EnumNarrowed<this, OptVariants<T>, "Some"> {
    return this.matches("Some");
  }

  isNone(): this is EnumNarrowed<this, OptVariants<T>, "None"> {
    return this.matches("None");
  }
}

Now takeVariantValue() returns value type is narrowed down by isSome() and isNone().

const opt = Some(100);

opt.takeVariantValue(); // number | null

if (opt.isSome()) {
  opt.takeVariantValue(); // number
}

if (opt.isNone()) {
  opt.takeVariantValue(); // null
}