How can I do this with a new and shiny typescript assertion function?

103 views Asked by At

I have the following code:

type Robot = RobotOn | RobotOff;

class RobotOn {
    public readonly state = "on";

    public speak() {
        console.log("heyman!");
    }
}

class RobotOff {
    public readonly state = "off"
}

function maybeSpeak(robot: Robot) {
    if (robot.state === "off") throw Error("robot can't speak when it's off!");

    robot.speak();
}

Now I would love to assert the state of the robot with a typescript assertion function! So something like this:

function maybeSpeak(robot: Robot) {
    assertRobotState(robot, "on");

    robot.speak();
}

Is this even possible? And what would the assertRobotState function look like?

Thanks!

2

There are 2 answers

1
adzo261 On

You can use assertion based on type of an argument - asserts <<arg>> is <<type>>.

function assertRobotStateOn(robot: Robot): asserts robot is RobotOn {
  if (robot.state !== 'on') {
    throw new Error('Robot is Off!');
  }
}
function assertRobotStateOff(robot: Robot): asserts robot is RobotOff {
   if (robot.state !== 'off') {
    throw new Error('Robot is On!');
  }
}
function maybeSpeak(robot: Robot) {
    assertRobotStateOn(robot);

    robot.speak();
}
maybeSpeak(new RobotOff());

References:

1
Elmer On

I have found an answer! It can be done with overloaded assertion functions! The following works:

type Robot = RobotOn | RobotOff;

class RobotOn {
    public readonly state = "on";

    public speak() {
        console.log("heyman!");
    }
}

class RobotOff {
    public readonly state = "off"
}

function maybeSpeak(robot: Robot) {
    assertRobotState(robot, "on");

    robot.speak();
}

function assertRobotState(robot: Robot, state: "on"): asserts robot is RobotOn;
function assertRobotState(robot: Robot, state: "off"): asserts robot is RobotOff;
function assertRobotState(robot: Robot, state: "on" | "off") {
    if (robot.state !== state) throw new Error("not cool");
}