tc39 / tc39/proposal-amount

Observable validation logic

Open
#92 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
HTML
Stars
58
Forks
12
Avg merge
2h 35m
Merged PRs (30d)
2

Description

It's a bit strange that e.g. GetAmountOptions rejects an invalid roundingMode value such as "foo" before even attempting to [[Get]] unit, but does not reject an invalid numeric fractionDigits such as 4.2 until after [[Get]]ing all options fields and verifying absence of a non-undefined value for significantDigits. I think it would make more sense (and be more aligned with recent additions such as Temporal) to perform isolated validation atomically with each [[Get]], leaving the door open for future consolidation that extends/wraps/post-processes GetOption similar to GetNumberOption, DefaultNumberOption, ToIntegerIfIntegral, and GetRoundingIncrementOption:

@@ GetAmountOptions (
           1. Let _opts_ be ? GetOptionsObject(_opts_).
           1. Let _fractionDigits_ be ? GetOption(_opts_, *"fractionDigits"*, ~number~, ~empty~, *undefined*).
+          1. If _fractionDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
           1. Let _roundingMode_ be ? GetOption(_opts_, *"roundingMode"*, ~string~, « *"ceil"*, *"floor"*, *"expand"*, *"trunc"*, *"halfCeil"*, *"halfFloor"*, *"halfExpand"*, *"halfTrunc"*, *"halfEven"* », *"halfEven"*).
           1. Let _significantDigits_ be ? GetOption(_opts_, *"significantDigits"*, ~number~, ~empty~, *undefined*).
+          1. If _significantDigits_ is not *undefined* and not a positive integral Number, throw a *RangeError* exception.
           1. Let _unit_ be ? GetOption(_opts_, *"unit"*, ~string~, ~empty~, *undefined*).
-          1. If _fractionDigits_ is not *undefined*, then
-            1. If _significantDigits_ is not *undefined*, throw a *RangeError* exception.
-            1. If _fractionDigits_ is not an integral Number, throw a *RangeError* exception.
-          1. Else if _significantDigits_ is not *undefined*, then
-            1. If _significantDigits_ is not an integral Number, throw a *RangeError* exception.
-            1. If _significantDigits_ &lt; *1*<sub>𝔽</sub>, throw a *RangeError* exception.
           1. If _unit_ is the empty String, throw a *RangeError* exception.
+          1. If _fractionDigits_ is not *undefined* and _significantDigits_ is not *undefined*, throw a *TypeError* exception.
           1. Return the Record { [[FractionDigits]]: _fractionDigits_, [[RoundingMode]]: _roundingMode_, [[SignificantDigits]]: _significantDigits_, [[Unit]]: _unit_ }.
@@ GetAmountConvertToOptions (
           1. Let _opts_ be ? GetOptionsObject(_opts_).
-          1. Let _minFractionDigits_ be ? GetOption(_opts_, *"minimumfractionDigits"*, ~number~, ~empty~, *undefined*).
+          1. Let _minFractionDigits_ be ? GetOption(_opts_, *"minimumFractionDigits"*, ~number~, ~empty~, *undefined*).
+          1. If _minFractionDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
-          1. Let _maxFractionDigits_ be ? GetOption(_opts_, *"maximumfractionDigits"*, ~number~, ~empty~, *undefined*).
+          1. Let _maxFractionDigits_ be ? GetOption(_opts_, *"maximumFractionDigits"*, ~number~, ~empty~, *undefined*).
+          1. If _maxFractionDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
           1. Let _roundingMode_ be ? GetOption(_opts_, *"roundingMode"*, ~string~, « *"ceil"*, *"floor"*, *"expand"*, *"trunc"*, *"halfCeil"*, *"halfFloor"*, *"halfExpand"*, *"halfTrunc"*, *"halfEven"* », *"halfEven"*).
           1. Let _roundingPriority_ be ? GetOption(_opts_, *"roundingPriority"*, ~string~, ~empty~, *undefined*).
           1. Let _minSignificantDigits_ be ? GetOption(_opts_, *"minimumSignificantDigits"*, ~number~, ~empty~, *undefined*).
+          1. If _minSignificantDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
           1. Let _maxSignificantDigits_ be ? GetOption(_opts_, *"maximumSignificantDigits"*, ~number~, ~empty~, *undefined*).
+          1. If _maxSignificantDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
-          1. Let _significantDigits_ be ? GetOption(_opts_, *"maximumSignificantDigits"*, ~number~, ~empty~, *undefined*).
           1. Let _unit_ be ? GetOption(_opts_, *"unit"*, ~string~, ~empty~, *undefined*).
-          1. If _minFractionDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
-          1. If _maxFractionDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
-          1. If _minSignificantDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
-          1. If _maxSignificantDigits_ is not *undefined* and not an integral Number, throw a *RangeError* exception.
           1. If _unit_ is the empty String, throw a *RangeError* exception.
-          1. Return the Record { [[MinimumFractionDigits]]: _minFractionDigits_, [[MaximumFractionDigits]]: _maxFractionDigits_, [[MinimumSignificantDigits]]: _minSignificantDigits_, [[MaximumSignificantDigits]]: _maxSignificantDigits_, [[RoundingMode]]: _roundingMode_, [[RoundingPriority]]: _roundingPriority_, [[SignificantDigits]]: _significantDigits_, [[Unit]]: _unit_ }.
+          1. Return the Record { [[MinimumFractionDigits]]: _minFractionDigits_, [[MaximumFractionDigits]]: _maxFractionDigits_, [[MinimumSignificantDigits]]: _minSignificantDigits_, [[MaximumSignificantDigits]]: _maxSignificantDigits_, [[RoundingMode]]: _roundingMode_, [[RoundingPriority]]: _roundingPriority_, [[Unit]]: _unit_ }.

(note also the alias/property key spelling corrections, removal of [[SignificantDigits]], and replacement of RangeError with TypeError to indicate conflicting options, aligning with e.g. CreateDateTimeFormat and SetNumberFormatDigitOptions)

Further, options should be read in alphabetical order, reördering GetAmountConvertToOptions like ["maximumFractionDigits", "maximumSignificantDigits", "minimumFractionDigits", "minimumSignificantDigits", "roundingMode", "roundingPriority"].

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the GetAmountOptions and GetAmountConvertToOptions algorithms in the proposal, then compare their validation and option-ordering behavior with the linked Temporal and ECMA-402 algorithms. Done means the specified validation is atomic and observable, property names and record fields are corrected, conflicting options use the intended error, and options are read in the requested order.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
documentation
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.