measurable 1.1.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +149 -0
- package/README.md +271 -53
- package/dist/dimensions/angle.js +13 -6
- package/dist/dimensions/area.d.ts +14 -0
- package/dist/dimensions/area.js +46 -0
- package/dist/dimensions/data.d.ts +10 -0
- package/dist/dimensions/data.js +39 -0
- package/dist/dimensions/energy.d.ts +14 -0
- package/dist/dimensions/energy.js +29 -0
- package/dist/dimensions/force.js +6 -6
- package/dist/dimensions/frequency.d.ts +7 -0
- package/dist/dimensions/frequency.js +11 -0
- package/dist/dimensions/illuminance.d.ts +9 -0
- package/dist/dimensions/illuminance.js +16 -0
- package/dist/dimensions/index.d.ts +9 -0
- package/dist/dimensions/index.js +9 -0
- package/dist/dimensions/length.js +7 -7
- package/dist/dimensions/luminance.d.ts +7 -0
- package/dist/dimensions/luminance.js +13 -0
- package/dist/dimensions/luminousIntensity.d.ts +9 -0
- package/dist/dimensions/luminousIntensity.js +16 -0
- package/dist/dimensions/mass.js +13 -9
- package/dist/dimensions/power.d.ts +9 -0
- package/dist/dimensions/power.js +13 -0
- package/dist/dimensions/pressure.d.ts +14 -0
- package/dist/dimensions/pressure.js +21 -0
- package/dist/dimensions/temperature.js +9 -3
- package/dist/dimensions/time.js +19 -7
- package/dist/dimensions/volume.js +57 -24
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/lib/Dimension.d.ts +49 -14
- package/dist/lib/Dimension.js +58 -21
- package/dist/lib/MeasurementSystem.js +2 -2
- package/dist/lib/Quantity.d.ts +94 -10
- package/dist/lib/Quantity.js +92 -37
- package/dist/lib/Rational.d.ts +58 -0
- package/dist/lib/Rational.js +174 -0
- package/dist/lib/Unit.d.ts +52 -11
- package/dist/lib/Unit.js +41 -8
- package/dist/lib/prefixes.d.ts +2 -2
- package/dist/lib/prefixes.js +1 -1
- package/dist/systems/imperial.js +19 -1
- package/dist/systems/metric.js +25 -1
- package/dist/systems/usCustomary.js +19 -1
- package/dist/utils/definePrefixed.d.ts +28 -0
- package/dist/utils/definePrefixed.js +72 -0
- package/dist/utils/scaleOf.d.ts +3 -0
- package/dist/utils/scaleOf.js +6 -0
- package/package.json +13 -4
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./errors/InvalidConversionError";
|
|
|
3
3
|
export * from "./errors/UnknownUnitError";
|
|
4
4
|
export * from "./lib/Dimension";
|
|
5
5
|
export * from "./lib/MeasurementSystem";
|
|
6
|
-
export * from "./lib/prefixes";
|
|
7
6
|
export * from "./lib/Quantity";
|
|
7
|
+
export * from "./lib/Rational";
|
|
8
8
|
export * from "./lib/Unit";
|
|
9
|
+
export * from "./utils/definePrefixed";
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ __exportStar(require("./errors/InvalidConversionError"), exports);
|
|
|
19
19
|
__exportStar(require("./errors/UnknownUnitError"), exports);
|
|
20
20
|
__exportStar(require("./lib/Dimension"), exports);
|
|
21
21
|
__exportStar(require("./lib/MeasurementSystem"), exports);
|
|
22
|
-
__exportStar(require("./lib/prefixes"), exports);
|
|
23
22
|
__exportStar(require("./lib/Quantity"), exports);
|
|
23
|
+
__exportStar(require("./lib/Rational"), exports);
|
|
24
24
|
__exportStar(require("./lib/Unit"), exports);
|
|
25
|
+
__exportStar(require("./utils/definePrefixed"), exports);
|
package/dist/lib/Dimension.d.ts
CHANGED
|
@@ -1,26 +1,44 @@
|
|
|
1
|
+
import { Rational } from "./Rational";
|
|
1
2
|
import { Unit } from "./Unit";
|
|
2
3
|
/** A linear unit with an additive offset (e.g. temperature scales). */
|
|
3
4
|
export interface AffineSpec {
|
|
4
|
-
/**
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Multiplier applied when converting a value to the base unit. Pass a
|
|
7
|
+
* {@link Rational} (e.g. `new Rational(5, 9)`) for ratios that a decimal
|
|
8
|
+
* cannot represent exactly.
|
|
9
|
+
*/
|
|
10
|
+
scale: number | Rational;
|
|
6
11
|
/** Constant added (in base units) after scaling. */
|
|
7
|
-
offset: number;
|
|
12
|
+
offset: number | Rational;
|
|
8
13
|
}
|
|
9
14
|
/** A fully custom transform pair for non-linear units. */
|
|
10
15
|
export interface CustomSpec {
|
|
11
16
|
toBase: (value: number) => number;
|
|
12
17
|
fromBase: (value: number) => number;
|
|
13
18
|
}
|
|
19
|
+
/** Optional descriptors for a unit: its symbol, plural, and extra parse aliases. */
|
|
20
|
+
export interface UnitDef {
|
|
21
|
+
/** Canonical symbol, e.g. `"g"`, `"km"`, `"°C"`. Also registered for parsing. */
|
|
22
|
+
symbol?: string;
|
|
23
|
+
/** Plural name, e.g. `"grams"`. Also registered for parsing. */
|
|
24
|
+
plural?: string;
|
|
25
|
+
/** Additional names this unit parses from (beyond name, symbol, and plural). */
|
|
26
|
+
aliases?: string[];
|
|
27
|
+
}
|
|
14
28
|
/**
|
|
15
29
|
* A dimension is a single *kind* of measurable quantity (length, volume, mass,
|
|
16
30
|
* temperature, …). It owns one canonical **base unit** that every other unit in
|
|
17
31
|
* the dimension is defined relative to, and it is the single place where all
|
|
18
32
|
* conversion math happens.
|
|
19
33
|
*
|
|
20
|
-
* Converting `A → B`
|
|
21
|
-
*
|
|
22
|
-
* and means each unit only ever stores its relationship
|
|
23
|
-
* redundant, drift-prone pair of factors.
|
|
34
|
+
* Converting `A → B` routes through the base unit — conceptually
|
|
35
|
+
* `B.fromBase(A.toBase(value))`. This gives transitive conversions for free
|
|
36
|
+
* (any unit ↔ any unit) and means each unit only ever stores its relationship
|
|
37
|
+
* to the base — never a redundant, drift-prone pair of factors. Linear and
|
|
38
|
+
* affine units carry that relationship as an exact {@link Rational} transform,
|
|
39
|
+
* so their conversions are done in rational arithmetic and collapsed to a float
|
|
40
|
+
* once at the end, avoiding the binary rounding of routing through the base.
|
|
41
|
+
* Only non-linear units (defined via {@link custom}) fall back to float.
|
|
24
42
|
*
|
|
25
43
|
* A dimension is distinct from a {@link MeasurementSystem} (metric/imperial/…):
|
|
26
44
|
* the dimension decides what *can convert*, while a measurement system is a tag
|
|
@@ -39,21 +57,38 @@ export declare class Dimension {
|
|
|
39
57
|
baseUnit?: Unit;
|
|
40
58
|
constructor(name: string);
|
|
41
59
|
/** Define the canonical base unit (identity transform). */
|
|
42
|
-
base(name: string,
|
|
60
|
+
base(name: string, def?: UnitDef): Unit;
|
|
43
61
|
/**
|
|
44
62
|
* Define a linear unit. `scale` is how many base units make up one of this
|
|
45
|
-
* unit (e.g. a kilometer is `1000` meters).
|
|
63
|
+
* unit (e.g. a kilometer is `1000` meters). Pass a {@link Rational} for a
|
|
64
|
+
* scale a decimal cannot represent exactly.
|
|
46
65
|
*/
|
|
47
|
-
unit(name: string, scale: number,
|
|
66
|
+
unit(name: string, scale: number | Rational, def?: UnitDef): Unit;
|
|
48
67
|
/** Define an affine unit (scale plus additive offset, e.g. °C against K). */
|
|
49
|
-
affine(name: string, { scale, offset }: AffineSpec,
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
|
|
68
|
+
affine(name: string, { scale, offset }: AffineSpec, def?: UnitDef): Unit;
|
|
69
|
+
/**
|
|
70
|
+
* Define a non-linear unit from an arbitrary, hand-written inverse transform
|
|
71
|
+
* pair. Reserve this for units that genuinely cannot be expressed as `value *
|
|
72
|
+
* scale + offset` — e.g. logarithmic scales (decibels, octaves). Linear and
|
|
73
|
+
* affine units should use {@link unit} / {@link affine} so conversions stay
|
|
74
|
+
* exact.
|
|
75
|
+
*/
|
|
76
|
+
custom(name: string, { toBase, fromBase }: CustomSpec, def?: UnitDef): Unit;
|
|
77
|
+
/** Convert a `number` value between two units of this dimension. */
|
|
53
78
|
convert(value: number, from: Unit, to: Unit): number;
|
|
79
|
+
/**
|
|
80
|
+
* Convert an exact {@link Rational} value between two units, routed through
|
|
81
|
+
* the base. Linear / affine units stay exact end-to-end (the result is never
|
|
82
|
+
* collapsed to a float here), which is what lets {@link Quantity} chain
|
|
83
|
+
* conversions without drift. A conversion touching a non-linear `custom` unit
|
|
84
|
+
* falls back to float math and recaptures the result as a rational.
|
|
85
|
+
*/
|
|
86
|
+
convertRational(value: Rational, from: Unit, to: Unit): Rational;
|
|
54
87
|
/** Resolve a name or alias to its candidate unit(s) (used by parsing). */
|
|
55
88
|
get(token: string): Unit[] | undefined;
|
|
56
89
|
has(unit: Unit): boolean;
|
|
90
|
+
/** Define a linear / affine unit from its exact rational transform. */
|
|
91
|
+
private defineLinear;
|
|
57
92
|
private define;
|
|
58
93
|
/** Append a name/alias → unit mapping; shared aliases accumulate candidates. */
|
|
59
94
|
private register;
|
package/dist/lib/Dimension.js
CHANGED
|
@@ -2,17 +2,26 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Dimension = void 0;
|
|
4
4
|
const InvalidConversionError_1 = require("../errors/InvalidConversionError");
|
|
5
|
+
const Rational_1 = require("./Rational");
|
|
5
6
|
const Unit_1 = require("./Unit");
|
|
7
|
+
/** The rational `0`. */
|
|
8
|
+
const zero = new Rational_1.Rational(0n);
|
|
9
|
+
/** The rational `1`. */
|
|
10
|
+
const one = new Rational_1.Rational(1n);
|
|
6
11
|
/**
|
|
7
12
|
* A dimension is a single *kind* of measurable quantity (length, volume, mass,
|
|
8
13
|
* temperature, …). It owns one canonical **base unit** that every other unit in
|
|
9
14
|
* the dimension is defined relative to, and it is the single place where all
|
|
10
15
|
* conversion math happens.
|
|
11
16
|
*
|
|
12
|
-
* Converting `A → B`
|
|
13
|
-
*
|
|
14
|
-
* and means each unit only ever stores its relationship
|
|
15
|
-
* redundant, drift-prone pair of factors.
|
|
17
|
+
* Converting `A → B` routes through the base unit — conceptually
|
|
18
|
+
* `B.fromBase(A.toBase(value))`. This gives transitive conversions for free
|
|
19
|
+
* (any unit ↔ any unit) and means each unit only ever stores its relationship
|
|
20
|
+
* to the base — never a redundant, drift-prone pair of factors. Linear and
|
|
21
|
+
* affine units carry that relationship as an exact {@link Rational} transform,
|
|
22
|
+
* so their conversions are done in rational arithmetic and collapsed to a float
|
|
23
|
+
* once at the end, avoiding the binary rounding of routing through the base.
|
|
24
|
+
* Only non-linear units (defined via {@link custom}) fall back to float.
|
|
16
25
|
*
|
|
17
26
|
* A dimension is distinct from a {@link MeasurementSystem} (metric/imperial/…):
|
|
18
27
|
* the dimension decides what *can convert*, while a measurement system is a tag
|
|
@@ -30,35 +39,57 @@ class Dimension {
|
|
|
30
39
|
this.index = new Map();
|
|
31
40
|
}
|
|
32
41
|
/** Define the canonical base unit (identity transform). */
|
|
33
|
-
base(name,
|
|
34
|
-
const unit = this.
|
|
42
|
+
base(name, def = {}) {
|
|
43
|
+
const unit = this.defineLinear(name, { scale: one, offset: zero }, def);
|
|
35
44
|
this.baseUnit = unit;
|
|
36
45
|
return unit;
|
|
37
46
|
}
|
|
38
47
|
/**
|
|
39
48
|
* Define a linear unit. `scale` is how many base units make up one of this
|
|
40
|
-
* unit (e.g. a kilometer is `1000` meters).
|
|
49
|
+
* unit (e.g. a kilometer is `1000` meters). Pass a {@link Rational} for a
|
|
50
|
+
* scale a decimal cannot represent exactly.
|
|
41
51
|
*/
|
|
42
|
-
unit(name, scale,
|
|
43
|
-
return this.
|
|
52
|
+
unit(name, scale, def = {}) {
|
|
53
|
+
return this.defineLinear(name, { scale: Rational_1.Rational.from(scale), offset: zero }, def);
|
|
44
54
|
}
|
|
45
55
|
/** Define an affine unit (scale plus additive offset, e.g. °C against K). */
|
|
46
|
-
affine(name, { scale, offset },
|
|
47
|
-
return this.
|
|
56
|
+
affine(name, { scale, offset }, def = {}) {
|
|
57
|
+
return this.defineLinear(name, { scale: Rational_1.Rational.from(scale), offset: Rational_1.Rational.from(offset) }, def);
|
|
48
58
|
}
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Define a non-linear unit from an arbitrary, hand-written inverse transform
|
|
61
|
+
* pair. Reserve this for units that genuinely cannot be expressed as `value *
|
|
62
|
+
* scale + offset` — e.g. logarithmic scales (decibels, octaves). Linear and
|
|
63
|
+
* affine units should use {@link unit} / {@link affine} so conversions stay
|
|
64
|
+
* exact.
|
|
65
|
+
*/
|
|
66
|
+
custom(name, { toBase, fromBase }, def = {}) {
|
|
67
|
+
return this.define(name, def, { toBase, fromBase });
|
|
52
68
|
}
|
|
53
|
-
/** Convert a value between two units of this dimension
|
|
69
|
+
/** Convert a `number` value between two units of this dimension. */
|
|
54
70
|
convert(value, from, to) {
|
|
71
|
+
return this.convertRational(Rational_1.Rational.from(value), from, to).toNumber();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Convert an exact {@link Rational} value between two units, routed through
|
|
75
|
+
* the base. Linear / affine units stay exact end-to-end (the result is never
|
|
76
|
+
* collapsed to a float here), which is what lets {@link Quantity} chain
|
|
77
|
+
* conversions without drift. A conversion touching a non-linear `custom` unit
|
|
78
|
+
* falls back to float math and recaptures the result as a rational.
|
|
79
|
+
*/
|
|
80
|
+
convertRational(value, from, to) {
|
|
55
81
|
if (!this.units.has(from) || !this.units.has(to)) {
|
|
56
82
|
throw new InvalidConversionError_1.InvalidConversionError(from, to);
|
|
57
83
|
}
|
|
58
84
|
if (from === to) {
|
|
59
85
|
return value;
|
|
60
86
|
}
|
|
61
|
-
|
|
87
|
+
if (from.linear && to.linear) {
|
|
88
|
+
// base = from.scale * value + from.offset; result = (base - to.offset) / to.scale
|
|
89
|
+
const base = from.linear.scale.times(value).plus(from.linear.offset);
|
|
90
|
+
return base.minus(to.linear.offset).dividedBy(to.linear.scale);
|
|
91
|
+
}
|
|
92
|
+
return Rational_1.Rational.from(to.fromBase(from.toBase(value.toNumber())));
|
|
62
93
|
}
|
|
63
94
|
/** Resolve a name or alias to its candidate unit(s) (used by parsing). */
|
|
64
95
|
get(token) {
|
|
@@ -67,17 +98,23 @@ class Dimension {
|
|
|
67
98
|
has(unit) {
|
|
68
99
|
return this.units.has(unit);
|
|
69
100
|
}
|
|
70
|
-
|
|
101
|
+
/** Define a linear / affine unit from its exact rational transform. */
|
|
102
|
+
defineLinear(name, linear, def) {
|
|
103
|
+
return this.define(name, def, { linear });
|
|
104
|
+
}
|
|
105
|
+
define(name, { symbol, plural, aliases = [] }, transform) {
|
|
71
106
|
for (const existing of this.units) {
|
|
72
107
|
if (existing.name === name) {
|
|
73
108
|
throw new Error(`Duplicate unit name "${name}" in dimension "${this.name}"`);
|
|
74
109
|
}
|
|
75
110
|
}
|
|
76
|
-
const unit = new Unit_1.Unit({ name, dimension: this,
|
|
111
|
+
const unit = new Unit_1.Unit({ name, dimension: this, symbol, plural, ...transform });
|
|
77
112
|
this.units.add(unit);
|
|
78
|
-
this
|
|
79
|
-
|
|
80
|
-
|
|
113
|
+
// Register every label this unit can be parsed from, de-duplicated so a unit
|
|
114
|
+
// never appears twice among a token's candidates (e.g. if symbol === name).
|
|
115
|
+
const tokens = new Set([name, symbol, plural, ...aliases].filter((t) => !!t));
|
|
116
|
+
for (const token of tokens) {
|
|
117
|
+
this.register(token, unit);
|
|
81
118
|
}
|
|
82
119
|
return unit;
|
|
83
120
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MeasurementSystem = void 0;
|
|
4
|
-
const
|
|
4
|
+
const scaleOf_1 = require("../utils/scaleOf");
|
|
5
5
|
/**
|
|
6
6
|
* A measurement system (metric, imperial, US customary, …) is a cross-dimension
|
|
7
7
|
* collection of units that share a real-world standard.
|
|
@@ -37,7 +37,7 @@ class MeasurementSystem {
|
|
|
37
37
|
* to the smallest unit when even that rounds below 1).
|
|
38
38
|
*/
|
|
39
39
|
express(quantity) {
|
|
40
|
-
const candidates = this.in(quantity.unit.dimension).sort((a, b) => (0,
|
|
40
|
+
const candidates = this.in(quantity.unit.dimension).sort((a, b) => (0, scaleOf_1.scaleOf)(a) - (0, scaleOf_1.scaleOf)(b));
|
|
41
41
|
if (candidates.length === 0) {
|
|
42
42
|
throw new Error(`Measurement system "${this.name}" has no ` +
|
|
43
43
|
`"${quantity.unit.dimension.name}" units to express in`);
|
package/dist/lib/Quantity.d.ts
CHANGED
|
@@ -1,22 +1,105 @@
|
|
|
1
1
|
import type { Dimension } from "./Dimension";
|
|
2
2
|
import type { MeasurementSystem } from "./MeasurementSystem";
|
|
3
|
+
import { Rational } from "./Rational";
|
|
3
4
|
import type { Unit } from "./Unit";
|
|
4
5
|
/** Options for {@link Quantity.parse}. */
|
|
5
6
|
export interface ParseOptions {
|
|
6
7
|
/** Preferred measurement system, used only to break ties on shared aliases. */
|
|
7
8
|
prefer?: MeasurementSystem;
|
|
8
9
|
}
|
|
10
|
+
/** Options for {@link Quantity.format}. */
|
|
11
|
+
export interface FormatOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Which label to render after the magnitude:
|
|
14
|
+
* - `"auto"` (default) — singular `name` when the magnitude is exactly ±1,
|
|
15
|
+
* otherwise the `plural`.
|
|
16
|
+
* - `"name"` — always the singular name.
|
|
17
|
+
* - `"plural"` — always the plural.
|
|
18
|
+
* - `"symbol"` — the unit's symbol.
|
|
19
|
+
*
|
|
20
|
+
* `plural`/`symbol` fall back to the unit's `name` when that field is unset.
|
|
21
|
+
* Labels are English/canonical; localize the *magnitude* via {@link locale} /
|
|
22
|
+
* {@link numberFormat}.
|
|
23
|
+
*/
|
|
24
|
+
unit?: "auto" | "name" | "plural" | "symbol";
|
|
25
|
+
/**
|
|
26
|
+
* BCP 47 locale(s) used to render the magnitude (e.g. `"de-DE"`, `["fr", "en"]`).
|
|
27
|
+
* Passed straight to `Number.prototype.toLocaleString`. When omitted, the
|
|
28
|
+
* runtime's default locale is used.
|
|
29
|
+
*/
|
|
30
|
+
locale?: string | string[];
|
|
31
|
+
/**
|
|
32
|
+
* `Intl.NumberFormat` options for the magnitude — precision
|
|
33
|
+
* (`minimumFractionDigits` / `maximumFractionDigits`), `style: "currency"`,
|
|
34
|
+
* grouping, and so on. Passed straight to `Number.prototype.toLocaleString`.
|
|
35
|
+
*/
|
|
36
|
+
numberFormat?: Intl.NumberFormatOptions;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The rendered pieces of a formatted quantity, as returned by
|
|
40
|
+
* {@link Quantity.formatParts}. Each is already a finished string; the caller
|
|
41
|
+
* decides how to assemble them (a plain join, JSX, a template, …).
|
|
42
|
+
*/
|
|
43
|
+
export interface FormattedParts {
|
|
44
|
+
/** The locale-formatted magnitude, e.g. `"1.234,5"`. */
|
|
45
|
+
magnitude: string;
|
|
46
|
+
/** The chosen unit label, e.g. `"kilometers"`, `"km"`. */
|
|
47
|
+
unit: string;
|
|
48
|
+
}
|
|
9
49
|
/** A magnitude paired with a unit (e.g. `5` `kilometer`). */
|
|
10
50
|
export declare class Quantity {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
51
|
+
readonly unit: Unit;
|
|
52
|
+
/**
|
|
53
|
+
* The magnitude as an exact {@link Rational} — the source of truth this
|
|
54
|
+
* quantity is built on. Conversions and arithmetic operate on it directly, so
|
|
55
|
+
* a chain like `q.to(a).to(b)` stays exact instead of accumulating the binary
|
|
56
|
+
* rounding of repeated float round trips. (Exact for linear / affine units; a
|
|
57
|
+
* conversion through a non-linear `custom` unit recaptures a float as a
|
|
58
|
+
* rational, so it is best-effort there.)
|
|
59
|
+
*/
|
|
60
|
+
readonly rational: Rational;
|
|
61
|
+
constructor(magnitude: number | Rational, unit: Unit);
|
|
62
|
+
/** The magnitude as a `number`, derived from {@link rational}. */
|
|
63
|
+
get magnitude(): number;
|
|
14
64
|
/** Return an equivalent quantity expressed in `target`. */
|
|
15
65
|
to(target: Unit): Quantity;
|
|
16
66
|
/** Return this quantity's raw magnitude expressed in `target`. */
|
|
17
67
|
in(target: Unit): number;
|
|
68
|
+
/** This quantity's exact magnitude expressed in `target`. */
|
|
69
|
+
private inRational;
|
|
18
70
|
/** Render as `"<magnitude> <unit name>"`, e.g. `"5 kilometer"`. */
|
|
19
71
|
toString(): string;
|
|
72
|
+
/**
|
|
73
|
+
* Render as `"<magnitude> <label>"`, choosing the label per `options.unit`
|
|
74
|
+
* (default `"auto"`: magnitude-aware singular/plural). Unlike {@link toString},
|
|
75
|
+
* this can use the unit's symbol or plural — e.g. `"5 grams"`, `"5 g"`,
|
|
76
|
+
* `"1 gram"`. Pass `locale` / `numberFormat` to localize the magnitude via
|
|
77
|
+
* `toLocaleString` — e.g. `format({ locale: "de-DE" })` → `"1.234,5 meters"`,
|
|
78
|
+
* or `format({ numberFormat: { maximumFractionDigits: 2 } })` for precision.
|
|
79
|
+
*
|
|
80
|
+
* For non-string output (e.g. JSX), use {@link formatParts} and assemble the
|
|
81
|
+
* pieces yourself.
|
|
82
|
+
*/
|
|
83
|
+
format(options?: FormatOptions): string;
|
|
84
|
+
/**
|
|
85
|
+
* Like {@link format}, but returns the rendered magnitude and label as
|
|
86
|
+
* separate strings instead of joining them, so the caller controls the
|
|
87
|
+
* assembly. Useful when a single string won't do — e.g. styling the magnitude
|
|
88
|
+
* in a React component:
|
|
89
|
+
*
|
|
90
|
+
* ```tsx
|
|
91
|
+
* const { magnitude, unit } = q.formatParts({ locale: "de-DE" });
|
|
92
|
+
* return <><b>{magnitude}</b> {unit}</>;
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
formatParts(options?: FormatOptions): FormattedParts;
|
|
96
|
+
/**
|
|
97
|
+
* Render the magnitude via `toLocaleString`. With no `locale`/`numberFormat`
|
|
98
|
+
* it uses the runtime's default locale; supply either for locale- and
|
|
99
|
+
* precision-aware formatting.
|
|
100
|
+
*/
|
|
101
|
+
private formatMagnitude;
|
|
102
|
+
private formatLabel;
|
|
20
103
|
/**
|
|
21
104
|
* Add another quantity, returned in *this* quantity's unit. The other operand
|
|
22
105
|
* is converted into this unit first, so the two may use different units of the
|
|
@@ -31,9 +114,9 @@ export declare class Quantity {
|
|
|
31
114
|
/** Subtract another quantity, returned in this quantity's unit. */
|
|
32
115
|
minus(other: Quantity): Quantity;
|
|
33
116
|
/** Scale this quantity by a dimensionless factor. */
|
|
34
|
-
times(factor: number): Quantity;
|
|
117
|
+
times(factor: number | Rational): Quantity;
|
|
35
118
|
/** Divide this quantity by a dimensionless divisor. */
|
|
36
|
-
dividedBy(divisor: number): Quantity;
|
|
119
|
+
dividedBy(divisor: number | Rational): Quantity;
|
|
37
120
|
/**
|
|
38
121
|
* Divide this quantity by `other` of the same dimension, yielding the
|
|
39
122
|
* dimensionless ratio between them — i.e. how many of `other` fit in this.
|
|
@@ -54,14 +137,15 @@ export declare class Quantity {
|
|
|
54
137
|
/** Alias for {@link minus}. */
|
|
55
138
|
sub(other: Quantity): Quantity;
|
|
56
139
|
/** Alias for {@link times}. */
|
|
57
|
-
mul(factor: number): Quantity;
|
|
140
|
+
mul(factor: number | Rational): Quantity;
|
|
58
141
|
/** Alias for {@link dividedBy}. */
|
|
59
|
-
div(divisor: number): Quantity;
|
|
142
|
+
div(divisor: number | Rational): Quantity;
|
|
60
143
|
/**
|
|
61
144
|
* Whether this quantity equals `other`, compared in this quantity's unit.
|
|
62
145
|
* Throws {@link InvalidConversionError} if the operands belong to different
|
|
63
|
-
* dimensions. Comparison is exact, so
|
|
64
|
-
*
|
|
146
|
+
* dimensions. Comparison is exact rational equality, so quantities that are
|
|
147
|
+
* mathematically equal compare equal even if reaching them involved a
|
|
148
|
+
* conversion that would have drifted in floating point.
|
|
65
149
|
*/
|
|
66
150
|
equals(other: Quantity): boolean;
|
|
67
151
|
/** Whether this quantity does not equal `other`. */
|
|
@@ -104,7 +188,7 @@ export declare class Quantity {
|
|
|
104
188
|
* - `"5 hr"` -> `Quantity(5, hour)`
|
|
105
189
|
* - `"5hr 20min"` -> `Quantity(320, minute)`
|
|
106
190
|
*
|
|
107
|
-
* Compound inputs are summed
|
|
191
|
+
* Compound inputs are summed (exactly) and returned in the *finest*
|
|
108
192
|
* (smallest-scale) unit present, so `"5hr 20min"` collapses to `320 minute`.
|
|
109
193
|
*
|
|
110
194
|
* When a token is a shared alias (e.g. `"ton"` → short ton & long ton), pass
|