Skip to content

fix: reject fractional amounts for currencies with no minor unit, and unbreak the docs generator - #430

Merged
forzagreen merged 2 commits into
mainfrom
fix/zero-subunit-currency-guard
Aug 21, 2026
Merged

fix: reject fractional amounts for currencies with no minor unit, and unbreak the docs generator#430
forzagreen merged 2 commits into
mainfrom
fix/zero-subunit-currency-guard

Conversation

@forzagreen

@forzagreen forzagreen commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Extracted from #428 so they can land on their own.

8 files, +139/−131.

1. Fractional amounts for zero-subunit currencies

Five currencies have no everyday minor unit, and all five mishandled a fractional amount. Reproduced on 5.1.2:

input output on 5.1.2
ko-KR toCurrency(0.99) 영원 zero won
vi-VN toCurrency(0.99) không đồng zero dong
fa-IR toCurrency(0.99) صفر ریال zero rial
id-ID toCurrency(0.99) nol rupiah zero rupiah
ja-JP toCurrency(0.99) 九十九銭 99 sen

The first four destructure only dollars from parseCurrencyValue and never read cents, so the fractional part is silently discarded — the caller gets a well-formed string naming an amount that isn't the one they passed. ja-JP does read it, and spells 銭 (sen), a unit demonetised in 1953.

Both are "well-formed but wrong" — the bug class the contract system exists to catch, and undetectable by fuzzing for malformed output. All five now throw RangeError. Loud beats silent, same as checkMax.

Breaking for anyone relying on the old behaviour, but the old behaviour was data loss.

No API surface change: these forms take no options today and still don't. ja-JP's fixture cases asserting the fictitious-sen output are removed.

2. npm run docs:languages is broken on main

typescript@7.0.2 removed the classic ts.createProgram/getTypeChecker API that buildOptionsIndex() used:

TypeError: Cannot read properties of undefined (reading 'ES2022')

Rewritten against typescript/unstable/sync (the tsgo-backed replacement) and typescript/unstable/ast, reusing the existing src/tsconfig.json so compiler options can't drift from what CI's checkJs enforces.

The regenerated LANGUAGES.md differs only in union member ordering — tsgo returns constituents sorted rather than in declaration order, so 'masculine' | 'feminine' renders as 'feminine' | 'masculine'. Left as the new API reports it rather than re-sorted, so the generator stays a faithful view of the checker.

Verification

  • npm test — 453 tests pass
  • npm run lint — clean
  • npm run docs:languages — runs again, output committed

Both commits are already in #428; that branch will rebase onto this and drop them.

…rencies with no minor unit

Five currencies have no everyday minor unit, and all five mishandled a
fractional amount. Reproduced on 5.1.2:

  ko-KR  toCurrency(0.99)  ->  '영원'              (zero won)
  vi-VN  toCurrency(0.99)  ->  'không đồng'        (zero dong)
  fa-IR  toCurrency(0.99)  ->  'صفر ریال'          (zero rial)
  id-ID  toCurrency(0.99)  ->  'nol rupiah'        (zero rupiah)
  ja-JP  toCurrency(0.99)  ->  '九十九銭'           (99 sen)

The first four destructured only `dollars` from parseCurrencyValue and never
read `cents`, so the fractional part was silently discarded — the caller gets
a well-formed string naming an amount that isn't the one they passed. ja-JP
did read it, and spelled 銭 (sen), a unit demonetised in 1953.

Both are "well-formed but wrong", the exact bug class this project's contract
system exists to catch, and neither is detectable by fuzzing for malformed
output. All five now throw RangeError — loud beats silent, the same
philosophy checkMax already applies to its own precondition.

Removes ja-JP's fixture cases that asserted the old fictitious-sen output.
No API surface changes: these forms take no options today and still don't.
…piler API

typescript@7.0.2 removed the classic ts.createProgram/getTypeChecker API that
buildOptionsIndex() relied on to read JSDoc option types, so `npm run
docs:languages` fails outright on main:

  TypeError: Cannot read properties of undefined (reading 'ES2022')

Rewrite it against typescript/unstable/sync (the tsgo-backed replacement) and
typescript/unstable/ast, reusing the project's existing src/tsconfig.json so
compiler options can't drift from what CI's checkJs already enforces.

The regenerated LANGUAGES.md differs only in union member ordering — tsgo
returns union constituents sorted rather than in declaration order, so
`'masculine' | 'feminine'` now renders as `'feminine' | 'masculine'`. Left as
the new API reports it rather than re-sorted here, so the generator stays a
faithful view of the checker.
@forzagreen
forzagreen merged commit 1fe4e02 into main Aug 21, 2026
8 checks passed
@forzagreen
forzagreen deleted the fix/zero-subunit-currency-guard branch August 21, 2026 20:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant