Currency
The currency module provides a fluent, immutable API for performing money arithmetic with correct decimal precision. It uses currency.js internally to avoid floating-point errors that are common with plain JavaScript number operations.
Every operation returns a new instance, so the original value is never changed.
Import
import { money, Money } from '@open-kerno/commons/currency';
Overview
Instead of calling new Money(...), use the money() factory function. It works the same way but reads more naturally in a chain.
const result = money(100, 'USD')
.add(50)
.multiply(1.1)
.divide(2)
.getValue(); // 82.5
money (factory function)
function money(value: number, currencyCode?: string): Money
| Parameter | Type | Default | Description |
|---|---|---|---|
value | number | — | The initial amount |
currencyCode | string | 'USD' | An ISO 4217 currency code (e.g. 'EUR', 'JPY') |
The value is rounded to the currency's canonical decimal places on creation. For example, USD uses 2 decimal places, JPY uses 0, and KWD uses 3.
Money class
add(value)
Adds value to the current amount and returns a new Money instance.
money(10, 'USD').add(5).getValue(); // 15
money(0.1, 'USD').add(0.2).getValue(); // 0.3 (not 0.30000000000000004)
subtract(value)
Subtracts value from the current amount and returns a new Money instance.
money(10, 'USD').subtract(3.5).getValue(); // 6.5
money(0.3, 'USD').subtract(0.1).getValue(); // 0.2
multiply(value)
Multiplies the current amount by value and returns a new Money instance.
The intermediate calculation uses enough decimal places to avoid floating-point drift before rounding to the currency scale.
money(10, 'USD').multiply(1.1).getValue(); // 11
money(1.23, 'USD').multiply(1.25).getValue(); // 1.54
divide(value)
Divides the current amount by value and returns a new Money instance.
money(10, 'USD').divide(3).getValue(); // 3.33
money(9, 'USD').divide(3).getValue(); // 3
pow(exponent)
Raises the current amount to the given exponent using fast exponentiation (binary method) for integer exponents, falling back to Math.pow for fractional ones. Returns a new Money instance rounded to the currency scale.
money(2, 'USD').pow(3).getValue(); // 8
money(2, 'USD').pow(-1).getValue(); // 0.5
money(2, 'USD').pow(0).getValue(); // 1
money(9, 'USD').pow(0.5).getValue(); // 3
money(1.1, 'USD').pow(2).getValue(); // 1.21
nonNegative()
Returns a new Money instance with the value clamped to 0 if the current value is negative or NaN.
money(-5, 'USD').nonNegative().getValue(); // 0
money(NaN, 'USD').nonNegative().getValue(); // 0
money(5, 'USD').nonNegative().getValue(); // 5
This is useful after a subtraction that could go below zero:
money(10, 'USD').subtract(20).nonNegative().getValue(); // 0
getValue()
Returns the current numeric value. This is the terminal method that exits the chain.
money(1.5, 'USD').add(1).getValue(); // 2.5
getCurrencyInfo()
Returns an object with the currency code and its decimal precision.
money(100, 'EUR').getCurrencyInfo(); // { code: 'EUR', decimals: 2 }
money(100, 'JPY').getCurrencyInfo(); // { code: 'JPY', decimals: 0 }
money(100, 'KWD').getCurrencyInfo(); // { code: 'KWD', decimals: 3 }
Interfaces
interface CurrencyInfo {
code: string;
decimals: number;
}
interface IMoneyOperations {
add(value: number): Money;
subtract(value: number): Money;
multiply(value: number): Money;
divide(value: number): Money;
pow(exponent: number): Money;
nonNegative(): Money;
getValue(): number;
getCurrencyInfo(): CurrencyInfo;
}
Immutability
Every method returns a new Money instance. The original is never modified.
const base = money(100, 'USD');
const increased = base.add(50);
base.getValue(); // 100 — unchanged
increased.getValue(); // 150
Supported currencies
The module includes decimal precision for all major ISO 4217 currency codes. When an unknown code is provided, it falls back to USD (2 decimal places).
Some examples:
| Code | Decimals | Example |
|---|---|---|
USD | 2 | 1.99 |
EUR | 2 | 1.99 |
JPY | 0 | 199 |
KWD | 3 | 1.999 |
BHD | 3 | 1.999 |
CLP | 0 | 199 |
Examples
Tax calculation
const price = money(49.99, 'USD');
const withTax = price.multiply(1.08); // 8% tax
withTax.getValue(); // 53.99
Discount and floor at zero
const price = money(10, 'USD');
const afterDiscount = price.subtract(15).nonNegative();
afterDiscount.getValue(); // 0
Multi-step order total
const subtotal = money(120, 'USD');
const shipping = 9.99;
const discount = 20;
const total = subtotal
.add(shipping)
.subtract(discount)
.multiply(1.07) // 7% tax
.getValue();
// total: 117.67
Zero-decimal currency (JPY)
money(1234.56, 'JPY').getValue(); // 1235 — rounded to 0 decimals
money(100, 'JPY').add(50.9).getValue(); // 151