measurable 2.0.0 → 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 CHANGED
@@ -5,6 +5,54 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.0.0] - 2026-06-18
9
+
10
+ This release adds value **formatting**. Units now carry a `symbol` and `plural`,
11
+ and a `Quantity` can render itself with magnitude-aware pluralization and
12
+ locale-aware number formatting. Threading symbol/plural through the unit builders
13
+ changes their signatures, hence the major bump.
14
+
15
+ ### Breaking
16
+
17
+ - **Unit builder methods take a `UnitDef` object instead of an aliases array.**
18
+ `Dimension.base` / `unit` / `affine` / `custom` now accept
19
+ `{ symbol?, plural?, aliases? }` as their final argument rather than a bare
20
+ `string[]` of aliases. Migrate `length.unit("inch", 0.0254, ["in", "inches"])`
21
+ to `length.unit("inch", 0.0254, { symbol: "in", plural: "inches" })`; plain
22
+ aliases still work via the `aliases` field.
23
+ - **`definePrefixed` takes the reference `Unit`, not a descriptor object.**
24
+ `definePrefixed(length, { name: "meter", symbol: "m" })` becomes
25
+ `definePrefixed(length, meter)` — it reads the name, symbol, and exact scale
26
+ straight off the unit. The `PrefixReference` type is removed.
27
+ - **`Unit`'s constructor options gained `symbol` / `plural`.** Only affects code
28
+ that calls `new Unit(...)` directly; units built through a `Dimension` are
29
+ unaffected.
30
+
31
+ ### Added
32
+
33
+ - **`Unit.symbol` and `Unit.plural`** — optional, first-class descriptors. Both
34
+ are also registered as parse tokens, so `Quantity.parse` accepts e.g. `"5 g"`
35
+ and `"5 grams"`.
36
+ - **`Quantity.format(options?)`** — renders `"<magnitude> <label>"`. `unit`
37
+ selects the label: `"auto"` (default — singular `name` at ±1, otherwise
38
+ `plural`), `"name"`, `"plural"`, or `"symbol"`, each falling back to `name`
39
+ when unset. The magnitude is rendered with `toLocaleString`; pass `locale`
40
+ and/or `numberFormat` (`Intl.NumberFormatOptions`) for locale- and
41
+ precision-aware output.
42
+ - **`Quantity.formatParts(options?)`** — the same options as `format`, but
43
+ returns the rendered `{ magnitude, unit }` as separate strings for custom
44
+ assembly (e.g. JSX).
45
+ - **`definePrefixed` derives each variant's `symbol` and `plural`** from the
46
+ reference unit (e.g. `meter` → `kilometer` / `km`), keeping the prefixed scale
47
+ exact via rational arithmetic.
48
+ - New exported types: `UnitDef`, `FormatOptions`, `FormattedParts`,
49
+ `BaseUnitOptions`, `UnitConversionOptions`, and `UnitOptions`.
50
+
51
+ ### Changed
52
+
53
+ - Added `assert-never` as a (small) runtime dependency, used for exhaustiveness
54
+ checking in formatting.
55
+
8
56
  ## [2.0.0] - 2026-06-18
9
57
 
10
58
  This release makes conversions and quantity arithmetic **exact**. Magnitudes and
@@ -94,6 +142,7 @@ to a `number` at the edge, so `foot → inch` is exactly `12` (not
94
142
  `MeasurementSystem`), string parsing via `Quantity.parse`, and the first set
95
143
  of built-in dimensions and measurement systems.
96
144
 
145
+ [3.0.0]: https://github.com/mhuggins/measurable/compare/v2.0.0...v3.0.0
97
146
  [2.0.0]: https://github.com/mhuggins/measurable/compare/v1.1.1...v2.0.0
98
147
  [1.1.1]: https://github.com/mhuggins/measurable/compare/v1.1.0...v1.1.1
99
148
  [1.1.0]: https://github.com/mhuggins/measurable/compare/v1.0.0...v1.1.0
package/README.md CHANGED
@@ -206,9 +206,65 @@ metric.express(new Quantity(5000, meter)); // Quantity(5, kilometer)
206
206
  imperial.express(new Quantity(5000, meter)); // Quantity(3.107…, mile)
207
207
  ```
208
208
 
209
- A `Quantity` also has a `toString()` that renders `"<magnitude> <unit name>"`
210
- (e.g. `new Quantity(5, kilometer).toString()` → `"5 kilometer"`), and `round(decimals)`
211
- to trim the magnitude for display (`new Quantity(1.6213, mile).round(2)` `1.62 mile`).
209
+ ## Formatting output
210
+
211
+ Each unit carries a canonical `symbol` (`"g"`, `"km"`, `"°C"`) and `plural`
212
+ (`"grams"`, `"kilometers"`) alongside its `name`, so a `Quantity` can be rendered the
213
+ way you want:
214
+
215
+ ```ts
216
+ import { Quantity } from "measurable";
217
+ import { gram } from "measurable/dimensions";
218
+
219
+ new Quantity(5, gram).toString(); // "5 gram" (always the bare name)
220
+ new Quantity(5, gram).format(); // "5 grams" (magnitude-aware)
221
+ new Quantity(1, gram).format(); // "1 gram" (singular at ±1)
222
+ new Quantity(5, gram).format({ unit: "symbol" }); // "5 g"
223
+ new Quantity(5, gram).format({ unit: "name" }); // "5 gram"
224
+ new Quantity(5, gram).format({ unit: "plural" }); // "5 grams"
225
+
226
+ // Localize the magnitude with locale / numberFormat (passed to toLocaleString):
227
+ new Quantity(1234.5, meter).format({ locale: "de-DE" }); // "1.234,5 meters"
228
+ new Quantity(1.23456, meter).format({ numberFormat: { maximumFractionDigits: 2 } }); // "1.23 meters"
229
+ new Quantity(1234.5, kilometer).format({ locale: "de-DE", unit: "symbol" }); // "1.234,5 km"
230
+ ```
231
+
232
+ `toString()` is intentionally stable (`"<magnitude> <name>"`). `format(options?)` is the
233
+ flexible one: `unit` defaults to `"auto"` (singular `name` at ±1, otherwise `plural`) and
234
+ accepts `"name"`, `"plural"`, or `"symbol"`. When a unit has no `symbol`/`plural`, those
235
+ modes fall back to its `name`.
236
+
237
+ The magnitude is rendered with `Number.prototype.toLocaleString`. Pass `locale` (a BCP 47
238
+ locale or array) and/or `numberFormat` (`Intl.NumberFormatOptions` — precision via
239
+ `maximumFractionDigits`, grouping, `style`, …) to control it; with neither set, the
240
+ runtime's default locale is used. Use `round(decimals)` to trim the magnitude first
241
+ (`new Quantity(1.6213, mile).round(2)` → `1.62 mile`).
242
+
243
+ When a single string won't do — e.g. styling the magnitude in a React component — use
244
+ `formatParts(options?)`, which takes the same options but returns the rendered
245
+ `{ magnitude, unit }` as separate strings for you to assemble:
246
+
247
+ ```tsx
248
+ const { magnitude, unit } = new Quantity(1234.5, kilometer).formatParts({ locale: "de-DE" });
249
+ // { magnitude: "1.234,5", unit: "kilometers" }
250
+ return <><b>{magnitude}</b> {unit}</>;
251
+ ```
252
+
253
+ ### Internationalization
254
+
255
+ `format()` localizes the **magnitude** (via `locale`/`numberFormat`), but the **label** it
256
+ appends — `symbol`/`plural` — is **English/canonical** convenience data, not a localization
257
+ system: a single plural string can't model languages with several plural forms, and the
258
+ names themselves are English. For a fully localized label, delegate to `Intl.NumberFormat`,
259
+ whose `style: "unit"` localizes **and** pluralizes a curated set of units for you:
260
+
261
+ ```ts
262
+ const q = new Quantity(5, kilometer);
263
+ new Intl.NumberFormat("de", { style: "unit", unit: "kilometer" }).format(q.magnitude);
264
+ // "5 Kilometer"
265
+ new Intl.NumberFormat("fr", { style: "unit", unit: "kilometer", unitDisplay: "short" })
266
+ .format(q.magnitude); // "5 km"
267
+ ```
212
268
 
213
269
  ## Parsing strings
214
270
 
@@ -343,17 +399,21 @@ b.clamp(a, new Quantity(2, kilometer)); // b bounded to [a, 2 km], in b's unit
343
399
  ## Defining your own units
344
400
 
345
401
  Create a `Dimension` and add units through its builder methods. `scale` is how
346
- many base units make up one of the unit being defined.
402
+ many base units make up one of the unit being defined. The optional final argument
403
+ is a definition object — `{ symbol?, plural?, aliases? }` — whose `symbol` and
404
+ `plural` feed `format()` and, like `aliases`, are also registered for parsing.
347
405
 
348
406
  ```ts
349
407
  import { Dimension, Quantity } from "measurable";
350
408
 
351
409
  const data = new Dimension("data");
352
- const byte = data.base("byte", ["B", "bytes"]); // the base unit (identity)
353
- const kilobyte = data.unit("kilobyte", 1024, ["KB"]);
354
- const megabyte = data.unit("megabyte", 1024 ** 2, ["MB"]);
410
+ const byte = data.base("byte", { symbol: "B", plural: "bytes" }); // base unit (identity)
411
+ const kilobyte = data.unit("kilobyte", 1024, { symbol: "KB", plural: "kilobytes" });
412
+ const megabyte = data.unit("megabyte", 1024 ** 2, { symbol: "MB", plural: "megabytes" });
355
413
 
356
- new Quantity(2, megabyte).in(kilobyte); // 2048
414
+ new Quantity(2, megabyte).in(kilobyte); // 2048
415
+ new Quantity(2, megabyte).format(); // "2 megabytes"
416
+ new Quantity(2, megabyte).format({ unit: "symbol" }); // "2 MB"
357
417
  ```
358
418
 
359
419
  A numeric `scale` is read as the exact decimal you wrote (`0.0254` → `254/10000`),
@@ -374,16 +434,19 @@ const third = ratio.unit("third", new Rational(1, 3)); // exact, not 0.3333…
374
434
  import { Dimension, Rational } from "measurable";
375
435
 
376
436
  const temperature = new Dimension("temperature");
377
- const kelvin = temperature.base("kelvin", ["K"]);
437
+ const kelvin = temperature.base("kelvin", { symbol: "K" });
378
438
  // value_in_base = value * scale + offset
379
- const celsius = temperature.affine("celsius", { scale: 1, offset: 273.15 }, ["C"]);
439
+ const celsius = temperature.affine("celsius", { scale: 1, offset: 273.15 }, {
440
+ symbol: "°C",
441
+ aliases: ["C"],
442
+ });
380
443
  // Fahrenheit's 5/9 isn't a terminating decimal — give it (and the derived
381
444
  // offset) as exact Rationals so conversions round-trip without drift.
382
445
  const scale = new Rational(5, 9);
383
446
  const fahrenheit = temperature.affine(
384
447
  "fahrenheit",
385
448
  { scale, offset: Rational.from(273.15).minus(new Rational(32).times(scale)) },
386
- ["F"],
449
+ { symbol: "°F", aliases: ["F"] },
387
450
  );
388
451
  ```
389
452
 
@@ -402,18 +465,22 @@ dim.custom("squared", {
402
465
 
403
466
  ### Generating SI prefixes
404
467
 
405
- `definePrefixed` adds the metric prefix ladder to a reference unit and returns the
406
- created units keyed by name (skipping any name that already exists). Pass
407
- `SI_SUBMULTIPLE_PREFIXES` to generate fractions only.
468
+ `definePrefixed` adds the metric prefix ladder to a reference **unit** and returns the
469
+ created units keyed by name (skipping any name that already exists). It reads the
470
+ reference's `name`, `symbol`, and scale straight off the unit (via `scaleOf`), so each
471
+ generated unit gets a derived symbol (`b` → `kb`) and plural too — even when the
472
+ reference isn't the base unit. Pass `SI_SUBMULTIPLE_PREFIXES` to generate fractions only.
408
473
 
409
474
  ```ts
410
475
  import { Dimension, Quantity, definePrefixed } from "measurable";
411
476
 
412
477
  const data = new Dimension("data");
413
- const bit = data.base("bit", ["b"]);
414
- const prefixed = definePrefixed(data, { name: "bit", symbol: "b", scale: 1 });
478
+ const bit = data.base("bit", { symbol: "b", plural: "bits" });
479
+ const prefixed = definePrefixed(data, bit);
415
480
 
416
- new Quantity(1, prefixed.kilobit).in(bit); // 1000 (SI kilo = 1e3)
481
+ new Quantity(1, prefixed.kilobit).in(bit); // 1000 (SI kilo = 1e3)
482
+ prefixed.kilobit.symbol; // "kb"
483
+ new Quantity(5, prefixed.kilobit).format({ unit: "symbol" }); // "5 kb"
417
484
  ```
418
485
 
419
486
  ### Tagging units into a measurement system
@@ -430,21 +497,26 @@ si.has(kilobyte); // true
430
497
  ### `Dimension`
431
498
 
432
499
  - `new Dimension(name)`
433
- - `.base(name, aliases?)` — define the canonical base unit
434
- - `.unit(name, scale, aliases?)` — linear unit (`scale` base units per unit; `number | Rational`)
435
- - `.affine(name, { scale, offset }, aliases?)` — linear with additive offset (each `number | Rational`)
436
- - `.custom(name, { toBase, fromBase }, aliases?)` — arbitrary inverse pair, for non-linear units
500
+ - `.base(name, def?)` — define the canonical base unit
501
+ - `.unit(name, scale, def?)` — linear unit (`scale` base units per unit; `number | Rational`)
502
+ - `.affine(name, { scale, offset }, def?)` — linear with additive offset (each `number | Rational`)
503
+ - `.custom(name, { toBase, fromBase }, def?)` — arbitrary inverse pair, for non-linear units
437
504
  - `.convert(value, from, to)` — convert a raw `number` between two of its units
438
505
  - `.convertRational(value, from, to)` → `Rational` — exact conversion between two of its units
439
506
  - `.get(token)` — units matching a name/alias (`Unit[] | undefined`)
440
507
  - `.has(unit)`, `.units`, `.baseUnit`
441
508
 
509
+ `def` is an optional `UnitDef`: `{ symbol?, plural?, aliases? }`. All three are
510
+ registered as parse tokens; `symbol`/`plural` are additionally stored on the `Unit`.
511
+
442
512
  ### `Unit`
443
513
 
444
514
  A passive handle, normally created via a dimension's builder methods rather than
445
515
  `new Unit` directly. Read-only properties:
446
516
 
447
517
  - `.name` — the unit's canonical name
518
+ - `.symbol?` — canonical symbol (e.g. `"g"`, `"km"`), if declared
519
+ - `.plural?` — plural name (e.g. `"grams"`), if declared
448
520
  - `.dimension` — the `Dimension` it belongs to
449
521
  - `.linear` → `{ scale: Rational; offset: Rational } | undefined` — the exact transform for linear/affine units (`undefined` for `custom` ones)
450
522
  - `.toBase(value)` → `number` — convert a value in this unit to base units
@@ -457,7 +529,9 @@ A passive handle, normally created via a dimension's builder methods rather than
457
529
  - `.rational` → `Rational` — the exact magnitude (source of truth)
458
530
  - `.to(target)` → `Quantity`
459
531
  - `.in(target)` → `number`
460
- - `.toString()` → `string` — e.g. `"5 kilometer"`
532
+ - `.toString()` → `string` — stable `"<magnitude> <name>"`, e.g. `"5 kilometer"`
533
+ - `.format({ unit?, locale?, numberFormat? })` → `string` — `unit`: `"auto"` (default, magnitude-aware) / `"name"` / `"plural"` / `"symbol"`; `locale` + `numberFormat` localize the magnitude via `toLocaleString`
534
+ - `.formatParts(options?)` → `{ magnitude, unit }` — same options as `.format`, but returns the rendered pieces separately for custom assembly (e.g. JSX)
461
535
  - `.plus(other)` / `.minus(other)` → `Quantity` — add/subtract another quantity (aliases: `add` / `sub`)
462
536
  - `.times(factor)` / `.dividedBy(divisor)` → `Quantity` — scale by a `number | Rational` (aliases: `mul` / `div`)
463
537
  - `.ratioTo(other)` → `number` — dimensionless ratio (how many of `other` fit in this)
@@ -5,10 +5,17 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Plane angle. Base unit: radian. */
7
7
  exports.angle = new Dimension_1.Dimension("angle");
8
- exports.radian = exports.angle.base("radian", ["rad", "radians"]);
9
- exports.degree = exports.angle.unit("degree", Math.PI / 180, ["deg", "°", "degrees"]);
10
- exports.gradian = exports.angle.unit("gradian", Math.PI / 200, ["grad", "gradians"]);
11
- exports.turn = exports.angle.unit("turn", 2 * Math.PI, ["turns", "revolution", "revolutions"]);
8
+ exports.radian = exports.angle.base("radian", { symbol: "rad", plural: "radians" });
9
+ exports.degree = exports.angle.unit("degree", Math.PI / 180, {
10
+ symbol: "°",
11
+ plural: "degrees",
12
+ aliases: ["deg"],
13
+ });
14
+ exports.gradian = exports.angle.unit("gradian", Math.PI / 200, { symbol: "grad", plural: "gradians" });
15
+ exports.turn = exports.angle.unit("turn", 2 * Math.PI, {
16
+ plural: "turns",
17
+ aliases: ["revolution", "revolutions"],
18
+ });
12
19
  /** SI-submultiple radians (milliradian, microradian, …); larger angles use degree/turn. */
13
- exports.metricAngle = (0, definePrefixed_1.definePrefixed)(exports.angle, { name: "radian", symbol: "rad" }, definePrefixed_1.SI_SUBMULTIPLE_PREFIXES);
20
+ exports.metricAngle = (0, definePrefixed_1.definePrefixed)(exports.angle, exports.radian, definePrefixed_1.SI_SUBMULTIPLE_PREFIXES);
14
21
  exports.milliradian = exports.metricAngle.milliradian, exports.microradian = exports.metricAngle.microradian;
@@ -4,22 +4,43 @@ exports.squareMile = exports.acre = exports.squareYard = exports.squareFoot = ex
4
4
  const Dimension_1 = require("../lib/Dimension");
5
5
  /** Area. Base unit: square meter. */
6
6
  exports.area = new Dimension_1.Dimension("area");
7
- exports.squareMeter = exports.area.base("squareMeter", ["m²", "m2", "square meter", "square meters"]);
7
+ exports.squareMeter = exports.area.base("squareMeter", {
8
+ symbol: "m²",
9
+ plural: "square meters",
10
+ aliases: ["m2", "square meter"],
11
+ });
8
12
  // Metric multiples/submultiples. Area scales as the square of the length
9
13
  // prefix, so these can't be generated by the linear SI prefix helper.
10
- exports.squareKilometer = exports.area.unit("squareKilometer", 1e6, ["km²", "km2"]);
11
- exports.hectare = exports.area.unit("hectare", 1e4, ["ha", "hectares"]);
12
- exports.are = exports.area.unit("are", 1e2, ["ares"]);
13
- exports.squareCentimeter = exports.area.unit("squareCentimeter", 1e-4, ["cm²", "cm2"]);
14
- exports.squareMillimeter = exports.area.unit("squareMillimeter", 1e-6, ["mm²", "mm2"]);
14
+ exports.squareKilometer = exports.area.unit("squareKilometer", 1e6, {
15
+ symbol: "km²",
16
+ aliases: ["km2"],
17
+ });
18
+ exports.hectare = exports.area.unit("hectare", 1e4, { symbol: "ha", plural: "hectares" });
19
+ exports.are = exports.area.unit("are", 1e2, { plural: "ares" });
20
+ exports.squareCentimeter = exports.area.unit("squareCentimeter", 1e-4, {
21
+ symbol: "cm²",
22
+ aliases: ["cm2"],
23
+ });
24
+ exports.squareMillimeter = exports.area.unit("squareMillimeter", 1e-6, {
25
+ symbol: "mm²",
26
+ aliases: ["mm2"],
27
+ });
15
28
  // Imperial / US customary.
16
- exports.squareInch = exports.area.unit("squareInch", 6.4516e-4, ["sq in", "in²", "in2"]);
17
- exports.squareFoot = exports.area.unit("squareFoot", 9.290304e-2, [
18
- "sq ft",
19
- "ft²",
20
- "ft2",
21
- "square feet",
22
- ]);
23
- exports.squareYard = exports.area.unit("squareYard", 0.83612736, ["sq yd", "yd²", "yd2"]);
24
- exports.acre = exports.area.unit("acre", 4046.8564224, ["ac", "acres"]);
25
- exports.squareMile = exports.area.unit("squareMile", 2589988.110336, ["sq mi", "mi²", "mi2"]);
29
+ exports.squareInch = exports.area.unit("squareInch", 6.4516e-4, {
30
+ symbol: "in²",
31
+ aliases: ["sq in", "in2"],
32
+ });
33
+ exports.squareFoot = exports.area.unit("squareFoot", 9.290304e-2, {
34
+ symbol: "ft²",
35
+ plural: "square feet",
36
+ aliases: ["sq ft", "ft2"],
37
+ });
38
+ exports.squareYard = exports.area.unit("squareYard", 0.83612736, {
39
+ symbol: "yd²",
40
+ aliases: ["sq yd", "yd2"],
41
+ });
42
+ exports.acre = exports.area.unit("acre", 4046.8564224, { symbol: "ac", plural: "acres" });
43
+ exports.squareMile = exports.area.unit("squareMile", 2589988.110336, {
44
+ symbol: "mi²",
45
+ aliases: ["sq mi", "mi2"],
46
+ });
@@ -4,9 +4,9 @@ exports.pebibyte = exports.pebibit = exports.tebibyte = exports.tebibit = export
4
4
  const Dimension_1 = require("../lib/Dimension");
5
5
  /** Digital information. Base unit: bit. */
6
6
  exports.data = new Dimension_1.Dimension("data");
7
- exports.bit = exports.data.base("bit", ["b", "bits"]);
8
- exports.nibble = exports.data.unit("nibble", 4, ["nibbles"]);
9
- exports.byte = exports.data.unit("byte", 8, ["B", "bytes"]);
7
+ exports.bit = exports.data.base("bit", { symbol: "b", plural: "bits" });
8
+ exports.nibble = exports.data.unit("nibble", 4, { plural: "nibbles" });
9
+ exports.byte = exports.data.unit("byte", 8, { symbol: "B", plural: "bytes" });
10
10
  // SI (decimal, 1000-based) and IEC (binary, 1024-based) multiples of the bit
11
11
  // and byte. SI uses the bare symbol (kb, kB); IEC uses the "i" infix (Kib, KiB).
12
12
  const SI_MULTIPLES = [
@@ -25,11 +25,14 @@ const IEC_MULTIPLES = [
25
25
  ];
26
26
  const multiples = {};
27
27
  for (const [prefix, symbol, factor] of [...SI_MULTIPLES, ...IEC_MULTIPLES]) {
28
- multiples[`${prefix}bit`] = exports.data.unit(`${prefix}bit`, factor, [`${symbol}b`, `${prefix}bits`]);
29
- multiples[`${prefix}byte`] = exports.data.unit(`${prefix}byte`, 8 * factor, [
30
- `${symbol}B`,
31
- `${prefix}bytes`,
32
- ]);
28
+ multiples[`${prefix}bit`] = exports.data.unit(`${prefix}bit`, factor, {
29
+ symbol: `${symbol}b`,
30
+ plural: `${prefix}bits`,
31
+ });
32
+ multiples[`${prefix}byte`] = exports.data.unit(`${prefix}byte`, 8 * factor, {
33
+ symbol: `${symbol}B`,
34
+ plural: `${prefix}bytes`,
35
+ });
33
36
  }
34
37
  /** Every SI and IEC multiple of the bit and byte, keyed by name. */
35
38
  exports.dataMultiples = multiples;
@@ -5,18 +5,25 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Energy. Base unit: joule. */
7
7
  exports.energy = new Dimension_1.Dimension("energy");
8
- exports.joule = exports.energy.base("joule", ["J", "joules"]);
9
- exports.calorie = exports.energy.unit("calorie", 4.184, ["cal", "calories"]);
10
- exports.kilocalorie = exports.energy.unit("kilocalorie", 4184, ["kcal", "Cal", "kilocalories"]);
11
- exports.britishThermalUnit = exports.energy.unit("britishThermalUnit", 1055.05585262, ["BTU", "Btu"]);
12
- exports.wattHour = exports.energy.unit("wattHour", 3600, ["Wh", "watt-hour", "watt-hours"]);
8
+ exports.joule = exports.energy.base("joule", { symbol: "J", plural: "joules" });
9
+ exports.calorie = exports.energy.unit("calorie", 4.184, { symbol: "cal", plural: "calories" });
10
+ exports.kilocalorie = exports.energy.unit("kilocalorie", 4184, {
11
+ symbol: "kcal",
12
+ plural: "kilocalories",
13
+ aliases: ["Cal"],
14
+ });
15
+ exports.britishThermalUnit = exports.energy.unit("britishThermalUnit", 1055.05585262, {
16
+ symbol: "BTU",
17
+ aliases: ["Btu"],
18
+ });
19
+ exports.wattHour = exports.energy.unit("wattHour", 3600, {
20
+ symbol: "Wh",
21
+ plural: "watt-hours",
22
+ aliases: ["watt-hour"],
23
+ });
13
24
  /** Every SI-prefixed joule (kilojoule, megajoule, millijoule, …), keyed by name. */
14
- exports.metricEnergy = (0, definePrefixed_1.definePrefixed)(exports.energy, { name: "joule", symbol: "J" });
25
+ exports.metricEnergy = (0, definePrefixed_1.definePrefixed)(exports.energy, exports.joule);
15
26
  exports.kilojoule = exports.metricEnergy.kilojoule, exports.megajoule = exports.metricEnergy.megajoule, exports.gigajoule = exports.metricEnergy.gigajoule, exports.millijoule = exports.metricEnergy.millijoule;
16
27
  /** Every SI-prefixed watt-hour (kilowatt-hour, megawatt-hour, …), keyed by name. */
17
- exports.metricWattHour = (0, definePrefixed_1.definePrefixed)(exports.energy, {
18
- name: "wattHour",
19
- symbol: "Wh",
20
- scale: 3600,
21
- });
28
+ exports.metricWattHour = (0, definePrefixed_1.definePrefixed)(exports.energy, exports.wattHour);
22
29
  exports.kilowattHour = exports.metricWattHour.kilowattHour, exports.megawattHour = exports.metricWattHour.megawattHour, exports.gigawattHour = exports.metricWattHour.gigawattHour;
@@ -5,10 +5,10 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Force. Base unit: newton. */
7
7
  exports.force = new Dimension_1.Dimension("force");
8
- exports.newton = exports.force.base("newton", ["N", "newtons"]);
9
- exports.dyne = exports.force.unit("dyne", 0.00001, ["dyn", "dynes"]);
10
- exports.poundForce = exports.force.unit("poundForce", 4.4482216152605, ["lbf"]);
11
- exports.kilogramForce = exports.force.unit("kilogramForce", 9.80665, ["kgf"]);
8
+ exports.newton = exports.force.base("newton", { symbol: "N", plural: "newtons" });
9
+ exports.dyne = exports.force.unit("dyne", 0.00001, { symbol: "dyn", plural: "dynes" });
10
+ exports.poundForce = exports.force.unit("poundForce", 4.4482216152605, { symbol: "lbf" });
11
+ exports.kilogramForce = exports.force.unit("kilogramForce", 9.80665, { symbol: "kgf" });
12
12
  /** Every SI-prefixed newton (kilonewton, meganewton, millinewton, …), keyed by name. */
13
- exports.metricForce = (0, definePrefixed_1.definePrefixed)(exports.force, { name: "newton", symbol: "N" });
13
+ exports.metricForce = (0, definePrefixed_1.definePrefixed)(exports.force, exports.newton);
14
14
  exports.meganewton = exports.metricForce.meganewton, exports.kilonewton = exports.metricForce.kilonewton, exports.millinewton = exports.metricForce.millinewton, exports.micronewton = exports.metricForce.micronewton;
@@ -5,7 +5,7 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Frequency. Base unit: hertz. */
7
7
  exports.frequency = new Dimension_1.Dimension("frequency");
8
- exports.hertz = exports.frequency.base("hertz", ["Hz"]);
8
+ exports.hertz = exports.frequency.base("hertz", { symbol: "Hz" });
9
9
  /** Every SI-prefixed hertz (kilohertz, megahertz, gigahertz, …), keyed by name. */
10
- exports.metricFrequency = (0, definePrefixed_1.definePrefixed)(exports.frequency, { name: "hertz", symbol: "Hz" });
10
+ exports.metricFrequency = (0, definePrefixed_1.definePrefixed)(exports.frequency, exports.hertz);
11
11
  exports.kilohertz = exports.metricFrequency.kilohertz, exports.megahertz = exports.metricFrequency.megahertz, exports.gigahertz = exports.metricFrequency.gigahertz, exports.terahertz = exports.metricFrequency.terahertz, exports.petahertz = exports.metricFrequency.petahertz, exports.millihertz = exports.metricFrequency.millihertz;
@@ -5,9 +5,12 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Illuminance. Base unit: lux. */
7
7
  exports.illuminance = new Dimension_1.Dimension("illuminance");
8
- exports.lux = exports.illuminance.base("lux", ["lx"]);
9
- exports.footCandle = exports.illuminance.unit("footCandle", 10.764, ["fc", "ft-c"]);
10
- exports.phot = exports.illuminance.unit("phot", 10000, ["ph", "phots"]);
8
+ exports.lux = exports.illuminance.base("lux", { symbol: "lx" });
9
+ exports.footCandle = exports.illuminance.unit("footCandle", 10.764, {
10
+ symbol: "fc",
11
+ aliases: ["ft-c"],
12
+ });
13
+ exports.phot = exports.illuminance.unit("phot", 10000, { symbol: "ph", plural: "phots" });
11
14
  /** Every SI-prefixed lux (kilolux, millilux, microlux, …), keyed by name. */
12
- exports.metricIlluminance = (0, definePrefixed_1.definePrefixed)(exports.illuminance, { name: "lux", symbol: "lx" });
15
+ exports.metricIlluminance = (0, definePrefixed_1.definePrefixed)(exports.illuminance, exports.lux);
13
16
  exports.kilolux = exports.metricIlluminance.kilolux, exports.millilux = exports.metricIlluminance.millilux, exports.microlux = exports.metricIlluminance.microlux;
@@ -5,12 +5,12 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Length / distance. Base unit: meter. */
7
7
  exports.length = new Dimension_1.Dimension("length");
8
- exports.meter = exports.length.base("meter", ["m", "meters"]);
8
+ exports.meter = exports.length.base("meter", { symbol: "m", plural: "meters" });
9
9
  // Imperial / US customary.
10
- exports.inch = exports.length.unit("inch", 0.0254, ["in", "inches"]);
11
- exports.foot = exports.length.unit("foot", 0.3048, ["ft", "feet"]);
12
- exports.yard = exports.length.unit("yard", 0.9144, ["yd", "yards"]);
13
- exports.mile = exports.length.unit("mile", 1609.344, ["mi", "miles"]);
10
+ exports.inch = exports.length.unit("inch", 0.0254, { symbol: "in", plural: "inches" });
11
+ exports.foot = exports.length.unit("foot", 0.3048, { symbol: "ft", plural: "feet" });
12
+ exports.yard = exports.length.unit("yard", 0.9144, { symbol: "yd", plural: "yards" });
13
+ exports.mile = exports.length.unit("mile", 1609.344, { symbol: "mi", plural: "miles" });
14
14
  /** Every SI-prefixed meter (kilometer, centimeter, micrometer, …), keyed by name. */
15
- exports.metricLength = (0, definePrefixed_1.definePrefixed)(exports.length, { name: "meter", symbol: "m" });
15
+ exports.metricLength = (0, definePrefixed_1.definePrefixed)(exports.length, exports.meter);
16
16
  exports.kilometer = exports.metricLength.kilometer, exports.hectometer = exports.metricLength.hectometer, exports.decameter = exports.metricLength.decameter, exports.decimeter = exports.metricLength.decimeter, exports.centimeter = exports.metricLength.centimeter, exports.millimeter = exports.metricLength.millimeter, exports.micrometer = exports.metricLength.micrometer, exports.nanometer = exports.metricLength.nanometer;
@@ -4,13 +4,10 @@ exports.nit = exports.stilb = exports.candelaPerSquareMeter = exports.luminance
4
4
  const Dimension_1 = require("../lib/Dimension");
5
5
  /** Luminance. Base unit: candela per square meter (the nit). */
6
6
  exports.luminance = new Dimension_1.Dimension("luminance");
7
- exports.candelaPerSquareMeter = exports.luminance.base("candelaPerSquareMeter", [
8
- "cd/m²",
9
- "cd/m2",
10
- "nit",
11
- "nits",
12
- "nt",
13
- ]);
14
- exports.stilb = exports.luminance.unit("stilb", 1e4, ["sb"]);
7
+ exports.candelaPerSquareMeter = exports.luminance.base("candelaPerSquareMeter", {
8
+ symbol: "cd/m²",
9
+ aliases: ["cd/m2", "nit", "nits", "nt"],
10
+ });
11
+ exports.stilb = exports.luminance.unit("stilb", 1e4, { symbol: "sb" });
15
12
  /** Alias for {@link candelaPerSquareMeter}. */
16
13
  exports.nit = exports.candelaPerSquareMeter;
@@ -5,12 +5,12 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Luminous intensity. Base unit: candela. */
7
7
  exports.luminousIntensity = new Dimension_1.Dimension("luminousIntensity");
8
- exports.candela = exports.luminousIntensity.base("candela", ["cd"]);
9
- exports.candlepower = exports.luminousIntensity.unit("candlepower", 1, ["cp", "CP"]);
10
- exports.hefnerkerze = exports.luminousIntensity.unit("hefnerkerze", 0.92, ["HK"]);
11
- /** Every SI-prefixed candela (kilocandela, millicandela, …), keyed by name. */
12
- exports.metricLuminousIntensity = (0, definePrefixed_1.definePrefixed)(exports.luminousIntensity, {
13
- name: "candela",
14
- symbol: "cd",
8
+ exports.candela = exports.luminousIntensity.base("candela", { symbol: "cd", plural: "candelas" });
9
+ exports.candlepower = exports.luminousIntensity.unit("candlepower", 1, {
10
+ symbol: "cp",
11
+ aliases: ["CP"],
15
12
  });
13
+ exports.hefnerkerze = exports.luminousIntensity.unit("hefnerkerze", 0.92, { symbol: "HK" });
14
+ /** Every SI-prefixed candela (kilocandela, millicandela, …), keyed by name. */
15
+ exports.metricLuminousIntensity = (0, definePrefixed_1.definePrefixed)(exports.luminousIntensity, exports.candela);
16
16
  exports.kilocandela = exports.metricLuminousIntensity.kilocandela, exports.millicandela = exports.metricLuminousIntensity.millicandela, exports.microcandela = exports.metricLuminousIntensity.microcandela;
@@ -9,16 +9,20 @@ const definePrefixed_1 = require("../utils/definePrefixed");
9
9
  * choice of base is internal and does not affect any conversion.)
10
10
  */
11
11
  exports.mass = new Dimension_1.Dimension("mass");
12
- exports.gram = exports.mass.base("gram", ["g", "grams"]);
12
+ exports.gram = exports.mass.base("gram", { symbol: "g", plural: "grams" });
13
13
  // Customary units, expressed in grams.
14
- exports.pound = exports.mass.unit("pound", 453.59237, ["lb", "lbs", "pounds"]);
15
- exports.ounce = exports.mass.unit("ounce", 28.349523125, ["oz", "ounces"]);
16
- exports.stone = exports.mass.unit("stone", 6350.29318, ["st", "stones"]);
17
- exports.tonne = exports.mass.unit("tonne", 1000000, ["t", "tonnes"]);
14
+ exports.pound = exports.mass.unit("pound", 453.59237, {
15
+ symbol: "lb",
16
+ plural: "pounds",
17
+ aliases: ["lbs"],
18
+ });
19
+ exports.ounce = exports.mass.unit("ounce", 28.349523125, { symbol: "oz", plural: "ounces" });
20
+ exports.stone = exports.mass.unit("stone", 6350.29318, { symbol: "st", plural: "stones" });
21
+ exports.tonne = exports.mass.unit("tonne", 1000000, { symbol: "t", plural: "tonnes" });
18
22
  // Short (US) and long (Imperial) tons share the "ton" alias but differ in size;
19
23
  // the metric tonne above is a separate unit again. Parsing disambiguates these.
20
- exports.shortTon = exports.mass.unit("shortTon", 907184.74, ["ton", "tons"]);
21
- exports.longTon = exports.mass.unit("longTon", 1016046.9088, ["ton", "tons"]);
24
+ exports.shortTon = exports.mass.unit("shortTon", 907184.74, { plural: "tons", aliases: ["ton"] });
25
+ exports.longTon = exports.mass.unit("longTon", 1016046.9088, { plural: "tons", aliases: ["ton"] });
22
26
  /** Every SI-prefixed gram — including the kilogram — keyed by name. */
23
- exports.metricMass = (0, definePrefixed_1.definePrefixed)(exports.mass, { name: "gram", symbol: "g" });
27
+ exports.metricMass = (0, definePrefixed_1.definePrefixed)(exports.mass, exports.gram);
24
28
  exports.kilogram = exports.metricMass.kilogram, exports.megagram = exports.metricMass.megagram, exports.hectogram = exports.metricMass.hectogram, exports.decagram = exports.metricMass.decagram, exports.decigram = exports.metricMass.decigram, exports.centigram = exports.metricMass.centigram, exports.milligram = exports.metricMass.milligram, exports.microgram = exports.metricMass.microgram, exports.nanogram = exports.metricMass.nanogram;
@@ -5,9 +5,9 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Power. Base unit: watt. */
7
7
  exports.power = new Dimension_1.Dimension("power");
8
- exports.watt = exports.power.base("watt", ["W", "watts"]);
9
- exports.horsepower = exports.power.unit("horsepower", 745.699872, ["hp"]);
10
- exports.metricHorsepower = exports.power.unit("metricHorsepower", 735.49875, ["PS"]);
8
+ exports.watt = exports.power.base("watt", { symbol: "W", plural: "watts" });
9
+ exports.horsepower = exports.power.unit("horsepower", 745.699872, { symbol: "hp" });
10
+ exports.metricHorsepower = exports.power.unit("metricHorsepower", 735.49875, { symbol: "PS" });
11
11
  /** Every SI-prefixed watt (kilowatt, megawatt, gigawatt, milliwatt, …), keyed by name. */
12
- exports.metricPower = (0, definePrefixed_1.definePrefixed)(exports.power, { name: "watt", symbol: "W" });
12
+ exports.metricPower = (0, definePrefixed_1.definePrefixed)(exports.power, exports.watt);
13
13
  exports.kilowatt = exports.metricPower.kilowatt, exports.megawatt = exports.metricPower.megawatt, exports.gigawatt = exports.metricPower.gigawatt, exports.terawatt = exports.metricPower.terawatt, exports.milliwatt = exports.metricPower.milliwatt;
@@ -5,14 +5,17 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Pressure. Base unit: pascal. */
7
7
  exports.pressure = new Dimension_1.Dimension("pressure");
8
- exports.pascal = exports.pressure.base("pascal", ["Pa", "pascals"]);
9
- exports.bar = exports.pressure.unit("bar", 1e5, ["bars"]);
10
- exports.millibar = exports.pressure.unit("millibar", 1e2, ["mbar", "millibars"]);
11
- exports.atmosphere = exports.pressure.unit("atmosphere", 101325, ["atm", "atmospheres"]);
12
- exports.torr = exports.pressure.unit("torr", 101325 / 760, ["Torr", "torrs"]);
13
- exports.psi = exports.pressure.unit("psi", 6894.757293168, ["lbf/in²", "lbf/in2"]);
14
- exports.inchOfMercury = exports.pressure.unit("inchOfMercury", 3386.389, ["inHg"]);
15
- exports.inchOfWater = exports.pressure.unit("inchOfWater", 249.0889, ["inAq"]);
8
+ exports.pascal = exports.pressure.base("pascal", { symbol: "Pa", plural: "pascals" });
9
+ exports.bar = exports.pressure.unit("bar", 1e5, { symbol: "bar", plural: "bars" });
10
+ exports.millibar = exports.pressure.unit("millibar", 1e2, { symbol: "mbar", plural: "millibars" });
11
+ exports.atmosphere = exports.pressure.unit("atmosphere", 101325, {
12
+ symbol: "atm",
13
+ plural: "atmospheres",
14
+ });
15
+ exports.torr = exports.pressure.unit("torr", 101325 / 760, { symbol: "Torr", plural: "torrs" });
16
+ exports.psi = exports.pressure.unit("psi", 6894.757293168, { aliases: ["lbf/in²", "lbf/in2"] });
17
+ exports.inchOfMercury = exports.pressure.unit("inchOfMercury", 3386.389, { symbol: "inHg" });
18
+ exports.inchOfWater = exports.pressure.unit("inchOfWater", 249.0889, { symbol: "inAq" });
16
19
  /** Every SI-prefixed pascal (kilopascal, hectopascal, megapascal, …), keyed by name. */
17
- exports.metricPressure = (0, definePrefixed_1.definePrefixed)(exports.pressure, { name: "pascal", symbol: "Pa" });
20
+ exports.metricPressure = (0, definePrefixed_1.definePrefixed)(exports.pressure, exports.pascal);
18
21
  exports.kilopascal = exports.metricPressure.kilopascal, exports.hectopascal = exports.metricPressure.hectopascal, exports.megapascal = exports.metricPressure.megapascal, exports.gigapascal = exports.metricPressure.gigapascal;
@@ -13,6 +13,6 @@ exports.temperature = new Dimension_1.Dimension("temperature");
13
13
  // rational arithmetic so conversions round-trip without drift.
14
14
  const fahrenheitScale = new Rational_1.Rational(5, 9);
15
15
  const fahrenheitOffset = Rational_1.Rational.from(273.15).minus(new Rational_1.Rational(32).times(fahrenheitScale));
16
- exports.kelvin = exports.temperature.base("kelvin", ["K"]);
17
- exports.celsius = exports.temperature.affine("celsius", { scale: 1, offset: 273.15 }, ["C", "°C"]);
18
- exports.fahrenheit = exports.temperature.affine("fahrenheit", { scale: fahrenheitScale, offset: fahrenheitOffset }, ["F", "°F"]);
16
+ exports.kelvin = exports.temperature.base("kelvin", { symbol: "K" });
17
+ exports.celsius = exports.temperature.affine("celsius", { scale: 1, offset: 273.15 }, { symbol: "°C", aliases: ["C"] });
18
+ exports.fahrenheit = exports.temperature.affine("fahrenheit", { scale: fahrenheitScale, offset: fahrenheitOffset }, { symbol: "°F", aliases: ["F"] });