impl/typescript.ts
1,858 bytes · the TypeScript implementation · view raw
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { type Money } from "./money_amount.ts"; ← from money.amount ^1.0.0 · built alongside by fune
import { integerToWords } from "./text_integer_to_words.ts"; ← from text.integer-to-words ^1.0.0 · built alongside by fune
interface UnitNames {
majorOne: string;
majorMany: string;
minorOne: string;
minorMany: string;
}
/**
* English unit names for the currencies this capability supports. All three
* have two decimal places, so the split below is by 100. A currency is not
* added here without its real unit names: they cannot be derived from a code.
*/
const UNITS: { [code: string]: UnitNames } = {
GBP: { majorOne: "pound", majorMany: "pounds", minorOne: "penny", minorMany: "pence" },
EUR: { majorOne: "euro", majorMany: "euros", minorOne: "cent", minorMany: "cents" },
USD: { majorOne: "dollar", majorMany: "dollars", minorOne: "cent", minorMany: "cents" },
};
/**
* Write a money amount in words, cheque style: 123456 GBP minor units is
* "one thousand two hundred and thirty-four pounds and fifty-six pence".
*/
export function amountInWords(amount: Money): string {
const units = Object.prototype.hasOwnProperty.call(UNITS, amount.currency) ? UNITS[amount.currency] : undefined;
if (units === undefined) {
throw new RangeError(`no words for currency "${amount.currency}"`);
}
// Validate the whole amount first, so an out-of-range value fails with the
// same message it would as a plain number.
integerToWords(amount.minor);
const magnitude = Math.abs(amount.minor);
const major = Math.floor(magnitude / 100);
const minor = magnitude % 100;
const parts: string[] = [];
if (major > 0 || minor === 0) {
parts.push(`${integerToWords(major)} ${major === 1 ? units.majorOne : units.majorMany}`);
}
if (minor > 0) {
parts.push(`${integerToWords(minor)} ${minor === 1 ? units.minorOne : units.minorMany}`);
}
const body = parts.join(" and ");
return amount.minor < 0 ? `minus ${body}` : body;
}