interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
declare function getSmallPet(): Fish | Bird;
// second example
type Foo = { foo: string }
type Bar = { bar: string }
type GetFooOrBar = () => Foo | Bar
In ReScript I understand that functions should return a single type, so returning Fish | Bird is a TypeScript concept but not a ReScript concept.
You might implement this using modules and Variants:
module Bird = {
type t
}
module Fish = {
type t
}
module Pet = {
type t = Bird(Bird.t) | Fish(Fish.t)
let getSmallPet = () => {
// TODO: Logic to return type Pet.t
}
}
The other methods would be implemented within the modules.
I think the answers are missing the important part of the question which is interop. I have been looking again over the past few days at interop with tagged/disjoint unions and to be honest there isn’t a good story other than writing manual encoders and decoders to convert from json (representing the TS side) into variants in rescript and vice versa. If anyone else knows any nice solutions I’m all ears, otherwise I’ll post a short example of what I’ve got so far once I’m back at a computer.
Often the best solution is to write a different binding for each type:
type fish
type bird
@module("foo") external getSmallPetFish: unit => fish = "getSmallPet"
@module("foo") external getSmallPetBird: unit => bird = "getSmallPet"
Most of the time, the return type can be safely determined based on the function arguments, so you would write the externals based on that. (Although not in this hypothetical example, since they just take unit, but in the real world this isn’t usually the case.)
But if it truly is impossible to statically determine what the return type is, then you can bypass the type system with raw JavaScript and do a runtime check to cast it to the appropriate type.
type bird
type fish
type t = Bird(bird) | Fish(fish)
let isBird = %raw(`function(x) { return "fly" in x }`)
@module("foo") external getSmallPetUnsafe: unit => 'a = "getSmallPet"
let getSmallPet = () => {
let pet = getSmallPetUnsafe()
if isBird(pet) {
Bird(pet)
} else {
Fish(pet)
}
}