jhugman / uniffi-bindgen-react-native

A uniffi bindings generator for calling Rust from react-native
Other
0 stars 0 forks source link

Enums with associated values and named associated values #48

Closed jhugman closed 1 month ago

jhugman commented 1 month ago

This changes the way enums with associated values (a.k.a. tagged unions) are represented in typescript.

Previously, they were raw objects, now they are classes.

This allows us to add extra methods, including handy constructors and instanceOf methods.

The interface for the MyEnum type is approximately { tag: MyEnum_tags, data: MyEnum_interface }, where MyEnum_tags is exported.

The tag allows us to to interesting pattern matching like switch statements.

e.g. consider an enum for Sheep, Goat(String, u32), Cat { owner: Option<String>, name: String }

The values are constructed like so:

const sheep = new Animal.Sheep();
const jeff = new Animal.Goat("Jeff", 17);
const roberta = new Animal.Cat({ name: "Roberta", owner: undefined });

For pattern matching, a simple tag enum is generated and exposed:

enum Animal_tags {
    Sheep, Goat, Cat
}

Typescript will infer the correct types of the value.data property:

function name(animal: Animal): string {
    switch (animal.tag) {
        case Animal_tags.Sheep:
            return "mah";
        case Animal_tags.Goat: {
            const [name, age] = animal.data;
            return name;
        }
        case Animal_tags.Cat: {
            const {name, owner} = animal.data;
            return name;
        }
        default:
            throw new Error("Unreachable");
    }
}

For convenience, a new method is also generated:

const berry = Animal.Cat.new({ name: "Berry", owner: undefined });

Having new as a method means that changing Animal from a flat enum (i.e. one without associated types) to one that does means only changing one side of the literal.

Also: instanceOf methods:

const ermintrude: any = Animal.Goat.new("Ermintrude", 18);

if (Animal.instanceOf(ermintrude)) {
    // ermintrude is an animal
}
if (Animal.Goat.instanceOf(ermintrude)) {
    // ermintrude is goat.
}