DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Subtitles: The Good, the Bad, and the Resource-Heavy
  • Integrating Jenkins With Playwright TypeScript: A Complete Guide
  • Efficiently Migrating From Jest to Vitest in a Next.js Project
  • Dynamic Forms With Camunda and Spring StateMachine

Trending

  • Failure Handling Mechanisms in Microservices and Their Importance
  • How to Configure and Customize the Go SDK for Azure Cosmos DB
  • My LLM Journey as a Software Engineer Exploring a New Domain
  • Kubeflow: Driving Scalable and Intelligent Machine Learning Systems
  1. DZone
  2. Data Engineering
  3. AI/ML
  4. Type Safe State Machines in TypeScript

Type Safe State Machines in TypeScript

State machines are an important and often-used implement in software development. Let's take a look at how to create one using TypeScript.

By 
David Karapetyan user avatar
David Karapetyan
·
Dec. 06, 18 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
12.1K Views

Join the DZone community and get the full member experience.

Join For Free

State machines are everywhere but I don't think they get enough air time. To remedy the situation I'm going to present an implementation in TypeScript and use it to model a very simple elevator.

I could point you to the definition of what a state machine is on Wikipedia but the name gives it away. State machines consist of states and a description of how to go from one state to the next. So let's start spelling things out.

The first thing we need is a description of the states. I'm going to use basic types like strings, numbers, and symbols because it greatly simplifies the rest of the implementation and we don't lose any generality

type Primitives = string | number | symbol;

Next we need a description of the transition function/map

type EventTypeMapping<I extends Primitives> = { [K in I]: unknown };

type TransitionMap<I extends Primitives, S, E extends EventTypeMapping<I>> = {
  [K in I]: (event: E[I], currentState: I, extraState: S) => I
};

That definition looks a bit elaborate but in plain English it says for every state we have a function that takes an event (E[I]), the current state ( I), some extra state ( S), and gives us back the next state. The events are indexed by the possible states the machine can be in because not every event is valid in every state. There is no way to actually enforce this constraint at compile time so our transition functions must deal with all event types which is what E[I] is about.

Now we can construct the machine. We'll use a class to model the machine with the above ingredients

export class Machine<I extends Primitives, S, E extends EventTypeMapping<I>> {

  constructor(
    readonly state: S,
    readonly initialState: I,
    readonly transitions: TransitionMap<I, S, E>,
    public currentState: I = initialState
  ) { }
}

The above machine doesn't do much because we are not leveraging the transition functions so let's remedy that.

// ...
  step(event: E[I]): [I, I] {
    const currentState = this.currentState;
    const newState = this.transitions[currentState](
      event, currentState, this.state);
    this.currentState = newState;
    return [currentState, newState];
  }
// ...

That's it. We just implemented an abstract state machine. Abstract state machines aren't that useful so let's implement a concrete one that models an elevator in a 3 story building.

The elevator will start on the 1st floor and then go all the way to the 3rd floor. After reaching the 3rd floor it will go all the way down to the 1st floor and then repeat this cycle. Like I said, it's very simple.

The first thing we need is the description of the states. The most relevant part of the state is the floor the elevator is on so that's what we'll use for the state description.

type Floor = 1 | 2 | 3;

This elevator doesn't take any inputs so the event mapping for each state is also very simple.

type AllowedEvents = { 1: void; 2: void; 3: void };

We need one more bit of information. The elevator needs to know which direction it's going.

type ExtraState = { direction: 'up' | 'down' };

The transitions functions are a runtime concern so to fill those in we're going to instantiate our abstract machine and fill them in.

const elevator = new Machine<Floor, ExtraState, AllowedEvents>(
  { direction: 'up' },
  1,
  {
    1: (e, f, s) => { return (s.direction = 'up', 2); }, // Can only go up
    2: (e, f, s) => { return s.direction === 'up' ? 3 : 1 }, // Can go up or down
    3: (e, f, s) => { return (s.direction = 'down', 2); } // Can only go down
  }
);

Let's also step through the transitions a few times to verify that it's working as we expect.

console.log(`*starting*`);
for (let i = 0; i < 12; i++) {
  const [previousFloor, nextFloor] = elevator.step(void(0));
  console.log(`Elevator going ${elevator.state.direction}: ${previousFloor} -> ${nextFloor}`);
  if (elevator.currentState === 1) {
    console.log(`---Cycle complete---`);
  }
}
console.log(`*done*`);
*starting*
Elevator going up: 1 -> 2
Elevator going up: 2 -> 3
Elevator going down: 3 -> 2
Elevator going down: 2 -> 1
---Cycle complete---
Elevator going up: 1 -> 2
Elevator going up: 2 -> 3
Elevator going down: 3 -> 2
Elevator going down: 2 -> 1
---Cycle complete---
Elevator going up: 1 -> 2
Elevator going up: 2 -> 3
Elevator going down: 3 -> 2
Elevator going down: 2 -> 1
---Cycle complete---
*done*

Looks correct to me. Making the elevator less simple is left as an exercise for the reader. See if you can model people requesting to be taken to a specific floor as a starting point.

All the code along with the above example lives at GitHub: davidk01/state-machine.

Machine TypeScript

Published at DZone with permission of David Karapetyan, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Subtitles: The Good, the Bad, and the Resource-Heavy
  • Integrating Jenkins With Playwright TypeScript: A Complete Guide
  • Efficiently Migrating From Jest to Vitest in a Next.js Project
  • Dynamic Forms With Camunda and Spring StateMachine

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!