Memento

An ability to provide and consume internal state via a predefined channel

The gist of the Memento pattern is that it allows to restore to a previous state of an object by providing state differences in a predetermined format and being able to process those differences. Sounds complex, let's dive right into an example. The code below is a Hangman game and you have three attempts to guess the word "SECRET" correctly. As you enter each guess, assuming it passes validation, a stack of "undos" gets another fragment. Then, if you enter "-" instead of a character, the game jumps back one character, and it keeps jumping back as long as the user keeps entering "-" at the prompt. At the same time, all methods and properties are private, the game is closed for modification.
This is the main Game class. It can only process letters:
class Game {
  private word: string;
  guesses: string[];
  numberOfErrors: number;
  private numberOfAllowedAttempts: number;
  private gameOver: boolean;
  supportUndo: boolean;
  message: string;

  constructor() {
    this.word = "SECRET";
    this.guesses = [];
    this.numberOfErrors = 0;
    this.numberOfAllowedAttempts = 3;
    this.supportUndo = false;
  }

  processGuess(guess: string) {
    if (guess.length !== 1) {
      this.message = "One letter at a time, please";
      return;
    }
    guess = guess.toUpperCase();
    if (!/^[a-zA-Z\-]+$/.test(guess)) {
      this.message = "alphabetic characters only, please";
      return;
    }
    if (this.guesses.indexOf(guess) >= 0) {
      this.message = `already guessed ${guess}. Try again`;
      return;
    }

    this.guesses.push(guess);

    if (this.word.indexOf(guess) >= 0) {
      this.message = "Good guess!";
      if (this.currentPuzzleState().indexOf("_") < 0) {
        this.message = `Victory! With only ${this.numberOfErrors} errors!`;
        this.gameOver = true;
      }
    } else {
      this.numberOfErrors++;
      this.message = `bad guess! This word contains no ${guess}. You have ${this.attemptsLeft()} guesses left`;
      if (this.attemptsLeft() === 0) {
        this.gameOver = true;
        this.message = `You lost. The word was: ${this.word.toUpperCase()}`;
      }
    }
  }

  private attemptsLeft(): number {
    return this.numberOfAllowedAttempts - this.numberOfErrors;
  }

  currentPuzzleState(): string {
    let result = "";
    for (let i = 0; i < this.word.length; i++) {
      if (this.guesses.indexOf(this.word[i].toString()) >= 0) {
        result += this.word[i].toString().toUpperCase() + " ";
      } else {
        result += "_ ";
      }
    }
    return result;
  }

  public gameIsOver(): boolean {
    return this.gameOver;
  }
}
The following simple class defines what we can save/restore to manipulate the game's internal state. I only need to keep the previously entered letters and a number of incorrect attempts.
class Memento {
  guesses: string[];
  numberOfErrors: number;
  constructor(guesses: string[], numberOfErrors: number) {
    this.guesses = guesses;
    this.numberOfErrors = numberOfErrors;
  }
}
The following class extends the Game class, it can create and process a state checkpoint.
class GameWithUndo extends Game {
  constructor() {
    super();
    this.supportUndo = true;
    console.log("undo supported!");
  }
  public createCheckPoint(): Memento {
    return new Memento([...this.guesses], this.numberOfErrors);
  }

  public processCheckPoint(checkPoint: Memento): void {
    this.guesses.length = 0;
    this.guesses.push(...checkPoint.guesses);
    this.numberOfErrors = checkPoint.numberOfErrors;
  }
}
Finally, the "Caretaker" component of the game, the code that runs it all:

let input = ".";
let g2 = new GameWithUndo(); //originator

let mementos = [] as Array;
mementos.push(g2.createCheckPoint());
console.log("Puzzle is: ", g2.currentPuzzleState());

while (!g2.gameIsOver()) {
  //this main program is the CareTaker
  input = prompt("enter a letter, or '-' to undo");
  console.log("Input: ", input);
  if (input === "-") {
    if (mementos.length > 1) {
      console.log("undoing...");
      let p = mementos.pop();
      let m = mementos[mementos.length - 1];
      g2.processCheckPoint(m);
      console.log(g2.currentPuzzleState());
    } else {
      console.log("cannot undo anymore");
    }
  } else {
    g2.processGuess(input);
    let m = g2.createCheckPoint();
    mementos.push(m);

    console.log(g2.message);
    console.log(g2.currentPuzzleState());
  }
}
A Memento object is created and stored at each turn, unless a user enters a "-". In that case, a Memento object is popped from the array that stores it and is processed.
This pattern follows the same principle as the Prototype pattern, which is the Open-Closed principle. Instead of opening a class for modification, provide a way to extract/consume required state. That way the class is still closed for modification, yet it provides a way to manipulate its internal state.
Source/Inspiration: Pluralsight