Functional Weave
Code in Rust

text.number-to-words@1.0.0

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;
}