Creating chainable calculations with the SafeMath class

I'm struggling to find the right keywords for my online search regarding this matter.

Recently, I developed a class containing safe math functions. Each function requires 2 arguments and returns the result after undergoing an assertion process.

For instance:

class SafeMath {

  static add(x: number, y: number) {
    let z: number = x + y;
    assert(z >= x, 'ds-math-add-overflow');
    return z;
  }

  static sub(x: number, y: number) {
    let z: number = x - y;
    assert(z <= x, 'ds-math-sub-underflow');
    return z;
  }

  static mul(x: number, y: number) {
    let z: number = x * y;
    assert(y == 0 || z / y == x, 'ds-math-mul-overflow');
    return z;
  }

  static div(x: number, y: number) {
    let z: number = x / y;
    assert(x > 0 || y > 0, 'ds-math-div-by-zero');
    return z;
  }

}

console.log(SafeMath.add(2,2)); // 4
console.log(SafeMath.sub(2,2)); // 0
console.log(SafeMath.mul(2,2)); // 4
console.log(SafeMath.div(2,2)); // 1

The aim behind these functions is to allow them to operate as follows:

let balance0: number = 1;
let balance1: number = 1;

let amount0In: number = 10;
let amount1In: number = 10;

let balance0Adjusted: number = balance0.mul(1000).sub(amount0In.mul(3));
let balance1Adjusted: number = balance1.mul(1000).sub(amount1In.mul(3));

In this scenario, the functions will take in y and utilize the preceding number as x.

Answer №1

If you need a solution, consider creating a wrapper function like this:

if (!Number.prototype.multiply)  // checking if the multiply method doesn't already exist 
  {
  Number.prototype.multiply = function(num){ return this * num }
  }
  
if (!Number.prototype.addition)
  {
  Number.prototype.addition = function(num){ return this + num }
  }
  
let value = 10
let result = value.multiply(2).addition(100)

console.log(result)

Answer №2

If you want to customize the functionality of Number.prototype, you can enhance it with additional methods to allow for chaining operations. It is generally advised against extending native objects using string property keys (refer to Why is extending native objects a bad practice?). To avoid potential conflicts and maintain uniqueness, consider using symbol property keys instead of strings.

For instance, here's a sample module that safely extends Number.prototype with a multiplication function utilizing a unique symbol, while also including the new function signature in the TypeScript Number interface:

mul.ts

const mul = Symbol("multiply");

function value(this: number, n: number) {
  return this * n;
}

declare global {
  interface Number {
    [mul]: typeof value;
  }
}

Object.defineProperty(Number.prototype, mul, { value });

export default mul;

Once you've created modules for subtraction, addition, division, etc., you can import them and leverage the exported unique symbols for chaining operations like so:

import mul from "./mul.ts";
import sub from "./sub.ts";

const balance = 1;
const amountIn = 10;
const balanceAdjusted = balance[mul](1000)[sub](amountIn[mul](3));
console.log(balanceAdjusted);
970

One advantage of having chainable math operations is the ability to combine them with the optional chaining operator, especially when dealing with nullish values.


Although it's possible to achieve similar results without using symbols, this approach may not be entirely safe against potential future JavaScript updates introducing built-in Number methods like mul:

mul.ts

function value(this: number, n: number) {
  return this * n;
}

declare global {
  interface Number {
    mul: typeof value;
  }
}

Object.defineProperty(Number.prototype, "mul", { value });

export {}; // something needs to be imported or exported for modularity
import "./mul.ts";
import "./sub.ts";

const balance = 1;
const amountIn = 10;
const balanceAdjusted = balance.mul(1000).sub(amountIn.mul(3));
console.log(balanceAdjusted);
970

To streamline the importing process, you can consolidate all modules into one combined module:

math.ts

export { default as mul } from "./mul.ts";
export { default as sub } from "./sub.ts";
/* add other modules here */

You can then import this module and selectively utilize the desired functions:

import { mul, sub } from "./math.ts";

const balance = 1;
const amountIn = 10;
const balanceAdjusted = balance[mul](1000)[sub](amountIn[mul](3));
console.log(balanceAdjusted);

Answer №3

Custom Number Functions Example

import { assert } from "https://deno.land/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="81f2f5e5c1b1afb0b1b3afb1">[email protected]</a>/testing/asserts.ts";

declare global {

  /*
    Extend global Number.prototype with custom functions

    Note: This approach is considered potentially unsafe as JavaScript might implement
    these functions in the future. It is recommended to use unique function names.

  */
  interface Number {
    add(n: number): number;
    sub(n: number): number;
    mul(n: number): number;
    div(n: number): number;
    pow(n: number): number;
    sqrt(): number;
    print(): number;
  }
}

Number.prototype.add = function(this:number, n:number) {

  assert((this + n) >= this, 'ds-math-add-overflow');
  return this + n;
}

Number.prototype.sub = function(this:number, n:number) {

  assert((this - n) <= this, 'ds-math-sub-underflow');
  return this - n;
}

Number.prototype.mul = function(this:number, n:number) {

  assert(n == 0 || (this * n) / n == this, 'ds-math-mul-overflow');
  return this * n;
}

Number.prototype.div = function(this:number, n:number) {

  assert(this > 0 || n > 0, 'ds-math-div-by-zero');
  return this / n;
}

Number.prototype.pow = function(this:number, n:number) {

  assert(this > 0 && n >= 2, 'ds-math-exp-to-zero');
  return this ** n;
}

// Babylonian method for square roots
Number.prototype.sqrt = function(this:number) {

  assert(this > 0, 'ds-math-sqrt-of-zero');

  let x: number = 0;
  let y: number = this;
  let z: number = 0;

  if (y > 3) {
    z = y;
    x = y / 2 + 1;
    while (x < z) {
      z = x;
      x = (y / x + x) / 2;
    }
  } else if (y != 0) {
    z = 1;
  }
  return z;
}

Number.prototype.print = function(this:number) {

  console.log('=', this);
  return this;
}

// Usage:

let balance = 0;

balance.add(10).print().sub(1).print().mul(2).print().div(3).print().pow(4).print().sqrt().print();

Output:

= 10
= 9
= 18
= 6
= 1296
= 36

Class Implementation Example

import { assert } from "https://deno.land/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="176463735727392627253927">[email protected]</a>/testing/asserts.ts";

class SafeMath {
  private n: number;

  constructor(start: number = 0) {
    this.n = start;
  }

  public add(y: number) {

    assert(this.n + y >= this.n, 'ds-math-add-overflow');

    let z: number = this.n + y;
    this.n = this.n + y;
    return this;
  }

  public sub(y: number) {

    assert(this.n - y <= this.n, 'ds-math-sub-underflow');

    let z: number = this.n - y;
    this.n = this.n - y;
    return this;
  }

  public mul(y: number) {

    assert(y == 0 || (this.n * y) / y == this.n, 'ds-math-mul-overflow');

    let z: number = this.n * y;
    this.n = this.n * y;
    return this;
  }

  public div(y: number) {

    assert(this.n > 0 || y > 0, 'ds-math-div-by-zero');

    let z: number = this.n / y;
    this.n = this.n / y;
    return this;
  }

  public pow(y: number) {

    assert(this.n > 0 && y >= 2, 'ds-math-exp-to-zero');

    let z: number = this.n ** y;
    this.n = this.n ** y;
    return this;
  }

  // Babylonian method for square roots
  public sqrt() {

    assert(this.n > 0, 'ds-math-sqrt-of-zero');

    let x: number = 0;
    let y: number = this.n;
    let z: number = 0;

    if (y > 3) {
      z = y;
      this.n = z;
      x = y / 2 + 1;
      while (x < z) {
        z = x;
        this.n = z
        x = (y / x + x) / 2;
      }
    } else if (y != 0) {
      z = 1;
      this.n = z
    }
    return this;
  }

  public print() {
    console.log('=', this.n);
    return this;
  }

}

// Usage:

new SafeMath(0).add(10).print().sub(1).print().mul(2).print().div(3).print().pow(4).print().sqrt().print();

Output:

= 10
= 9
= 18
= 6
= 1296
= 36

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Use jQuery to retrieve the number of items that have been selected in an ASP ListBox

Struggling to find a way to calculate the count of selected items from an ASP listbox using jQuery. Currently, encountering an issue where I am receiving "Cannot get property length of undefined or null reference" on the selectedOptions property. The var l ...

Calculating the size of an array based on its attributes

Hey there, I'm working with an array containing attributes and need to determine its length. In this particular case, the array should have a length of 2 since there are only two "items" present. {"items":[{"value":"2","valor":0,"name":"Limpeza"}, {" ...

Generate your API with the NODEJS Express application generator

The current functionality of the Express JS Express application generator allows for the generation of an initial App, also known as a Boilerplate, using the npx express-generator or express myapp commands depending on the version of Express. The default s ...

An unexpected error occurred in the Angular unit and integration tests, throwing off the script

I seem to be facing a recurring issue while running unit/integration tests for my Angular project using Karma. The tests have a 50:50 success/failure rate, working fine on my machine but failing consistently on our build server, making the process quite un ...

Converting a space-separated string into tags-input (treating each segment as a separate tag) in AngularJS

I need assistance with pasting a copied string into a tags-input element, like the example below: "[email protected] [email protected] [email protected] [email protected]": https://i.sstatic.net/0aFD5.jpg When I click outside of the tags-input element, I ...

Unable to send multiple cookies using custom headers in Next.js configuration

I am using custom headers to set the cookie in my next.config.js file. The refresh token is successfully set, but for some reason the second token is not being recognized. key: 'Set-Cookie', value: `RefreshTokenKey = " ...

Tips for loading JavaScript files after a view has been rendered in Angular

Just starting out with angular.js and currently working on my first project using Angular. I am in the process of creating a single page application with the following pages: Index.html - the main page for rendering Home page - containing the content for ...

Sort and incorporate elements by multiple strings present in an array

Looking to filter and include strings in an array by matching them with items in another array? Here is a sample code snippet that filters based on a single string: const filteredString = `${this.filter}`.toLowerCase(); this.filteredCampaigns = this. ...

Arrow functions in ECMAScript 6

var createTempItem = id => ({ id: id, name: "Temporary Product" }); I understand that the arrow function above is the same as: var createTempItem = function(id) { return { id: id, name: "Temporary Product" }; }; However, I am ...

Issues with jQuery's .attr function not resolving

I am currently tackling a practice problem on jsFiddle, and you can find it at http://jsfiddle.net/Q9yHD/ My aim is to implement a border around my table using the following code: $("#" + $id).find(".name").attr("border", "thick"); Unfortunately, I am f ...

Text field suddenly loses focus upon entering a single character

In my current code, I have functions that determine whether to display a TextField or a Select component based on a JSON value being Text or Select. However, I am facing an issue where I can only enter one letter into the TextField before losing focus. Sub ...

Unable to validate JWT - UnhandledPromiseRejectionWarning: JsonWebTokenError: invalid token format

One of the functions I have is responsible for sending data to a database (my posts). To ensure security, I use private and public keys to sign and verify tokens. Although I can successfully send this token from the front-end to the back-end via the header ...

A new marker has been created on the Ajax Google Map, however, the old marker is still displaying as

Hey, I'm currently working on retrieving marker latitudes and longitudes using Ajax. I am receiving Ajax data every second and successfully creating markers within a specific radius. However, I'm running into an issue with updating marker positio ...

A guide on simulating childprocess.exec in TypeScript

export function executeCommandPromise(file: string, command: string) { return new Promise((resolve, reject) => { exec(command, { cwd: `${file}` }, (error: ExecException | null, stdout: string, stderr: string) => { if (error) { con ...

A step-by-step guide on accessing and displaying local Storage JSON data in the index.html file of an Angular project, showcasing it on the

I am facing an issue with reading JSON data from localStorage in my Angular index.html file and displaying it when viewing the page source. Below is the code I have attempted: Please note that when checking the View page source, only plain HTML is being ...

What is the best way to manage the back button using jQuery?

I'm currently facing a challenge when it comes to managing the Browser's History. While plugins like History.js can be helpful for smaller tasks, I find myself struggling with more complex scenarios. Let me provide an example: Imagine I have a m ...

Determine the presence of a value in Javascript

Is there a way to block a Javascript alert from popping up when the value is undefined? For example: if (typeof message !== 'undefined') { alert(message); } ...

Conceal the element if the output of express is void

I am currently developing an app using nodejs and express. I am retrieving JSON data from an endpoint and displaying it on the page based on the values received. The issue I am facing is that if I receive a null or undefined value from the JSON data, how ...

MacGyver's innovative Angular mac-autocomplete directive fails to properly auto-complete

I have incorporated the .css and .js files for MacGyver in my HTML document. Additionally, I have added 'Mac' as a dependency in my Angular application. The following code snippet is included in my HTML: <mac-autocomplete ng-model="selected" ...

What is the best way to retrieve data from multiple pages of a JSON API in one go?

Why am I only receiving the first page out of 61 pages in this json file with my current call? How can I retrieve all of them? Is there a way to use axios in a loop or another method? <template> <div id="app"> <the ...