Home > Back-end >  How should I go about method chaining in TypeScript (like how Tiptap does it)
How should I go about method chaining in TypeScript (like how Tiptap does it)

Time:01-09

Tiptap (and many other text editors based on ProseMirror) has this amazing API where we can chain commands.

For example:

editor.chain().focus().toggleItalic().run()

Here, calling chain() returns all the chainable commands which themselves return chainable commands and that's how the chain goes on and on until you call run().

After calling run(), the commands between chain() and run() are executed in sync.

What's interesting (as far as my knowledge goes) is that this isn't a class. All of this can also be extended (including typings) by creating custom extensions for Tiptap. See this bold extension and its types.

I did go thru there source but wasn't able to comprehend much.

I really liked this design pattern and was wondering how should I go about implementing this in general...

Say, I wanted to implement a calculator on this pattern. How can I make this line work?

calculator.chain().setValue(10).add(3).sub(12).run() // 1

Thank you!

CodePudding user response:

There's lots of ways you could potentially implement this, here is just one with a simple object. A class encapsulation would propbably be a good idea. So long as you keep your methods contained within a single object, you can keep returning it and adding to the callback queue, which will eventually be executed on .run() and break the loop.

const calculator = {
  state: { value: 0 },
  callbacks: [] as ((...args: any[]) => any)[],
  _exec: {
    setValue: (val: number) => (calculator.state.value = val),
    add: (val: number) => (calculator.state.value  = val),
    sub: (val: number) => (calculator.state.value -= val),
  },
  _queue: {
    setValue: (val: number) => {
      calculator.callbacks.push(() => calculator._exec.setValue(val));
      return calculator._queue;
    },
    add: (val: number) => {
      calculator.callbacks.push(() => calculator._exec.add(val));
      return calculator._queue;
    },
    sub: (val: number) => {
      calculator.callbacks.push(() => calculator._exec.sub(val));
      return calculator._queue;
    },
    run: () => {
      calculator.callbacks.forEach((cb) => cb());
      calculator.callbacks = [];
    },
  },
  chain: () => calculator._queue,
};

calculator.chain().setValue(10).add(3).sub(12).run();
console.log(calculator.state.value); // logs 1
  •  Tags:  
  • Related