【JavaScript】単位をJSで取り扱えるようにしようという提案 #日本語訳 – Qiita

たとえば1t + 2kg + 3g + 4mgみたいなことはJavaScriptではできません。
単位変換ができない以前に、単位という概念がありません。
例外はくらいで、それ以外の値については値しか扱うことができません。

ということでJavaScriptで単位を扱えるようにしようというproposalが提出されました。
2025年11月現在のstageは1で、実装されるにしてもまだまだ先になりそうですが、面白そうな提案だったので紹介してみます。

ということで以下は該当のproposal、Representing Measuresです。
プロジェクト名はamountなのにタイトルはMeasure、よくわからぬ。

Goals and needs

現実世界では、数値が数値だけで存在している状況は稀です。
数値は、たいてい単位と共に使用されます。
ボウルの中のリンゴの数、コップ一杯の水の量、電気自動車の消費電力まで、あらゆるものが対象になります。
また、物理量の測定には精度、すなわち有効桁数も存在します。

Intlフォーマッタは数値をフォーマットすることはできますが、その単位は扱わないため、実世界に適用するとバグります。

我々は、単位を表す新しいオブジェクトを導入し、文字列表現を生成できるようにします。

一般的なユースケースとしては、以下のようなものが考えられます。

・計測値の精度を制御する。
・通貨の単位を扱う。ユーザは金額の数値だけではなく、その通貨単位を一緒に扱いたいでしょう。
・計測値を文字列にフォーマットして出力する。

Description

数値、精度、単位を含むプリミティブ型Amountを導入します。

Properties

以下のプロパティを持ちます。

・unit:string|undefined
単位。

・significantDigits:int
有効桁数。正の整数。

・fractionalDigits:int
小数点以下の桁数。非負整数。

Constructor

new Amount(value[, options])
コンストラクタは数値valueと、以下を含むオプション引数optionsを受け取ります。

・unit:string|undefined
単位。

・significantDigits:int
有効桁数。正の整数。

・fractionalDigits:int
小数点以下の桁数。非負整数。

・roundingMode
Intlが対応している、端数の丸めモード。

オブジェクトは、以下のメソッドを持ちます。

・toString([ options ])
文字列表現を返します。
デフォルトでは単位を角括弧で囲った1.23[kg]のような形式になります。

・toLocaleString(locale[, options])
ロケールを考慮した文字列表現を返します。
たとえば小数点がカンマになるロケールであれば1,23[kg]のようになります。

・with(options)
オプションを追加した新しい単位を作成します。

Examples

まずは単位のない数値の例です。

let a = new Amount("123.456");
a.fractionDigits;              // 3
a.significantDigits;           // 6
a.with({ fractionDigits: 4 }).toString(); // "123.4560"

有効桁数を増やすと、末尾に0が増えます。

単位付きの例です。

let a = new Amount("42.7", { unit: "kg" });
a.toString();                     // "42.7[kg]"
a.toString({ numberOnly: true }); // "42.7"

Formatting with Intl

Amountは、データ・ロケール・表示オプションを分離することで、国際フォーマットのデザインパターンを向上させます。

Amountを使わない場合、引数の目的が混在してしまいます。

let numberOfKilograms = 42.7;
let locale = "zh-CN";

let localizedString = new Intl.NumberFormat(locale, {
    minimumSignificantDigits: 4,
    style: "unit",
    unit: "kilogram",
    unitDisplay: "long",
})
.format(numberOfKilograms);
console.log(localizedString);  // "42.70千克"

Amountを使うと、目的が分離され、正しく操作しやすくなります。

// データモデル
let amt = new Amount("42.7", { unit: "kilogram", significantDigits: 4 });

// ロケール
let locale = "zh-CN";

// オプション
let options = { unitDisplay: "long" };

// まとめる
let localizedString = amt.toLocaleString(locale, options);
console.log(localizedString);  // "42.70千克"

Amountは、Intl.MessageFormatへの組み込みを、当初はユーザランドで、最終的に標準実装での組み込みを見据えています。

Selecting Plural Forms

i18nのよくある問題として、Intl.PluralRulesIntl.NumberFormatの両方に同じ精度を設定をしないといけないことが挙げられます。

// バグがあります。どうしてかわかりますか?
let locale = "en-US";
let numberOfStars = 1;
let numberString = new Intl.NumberFormat(locale, { minimumFractionDigits: 1 }).format(numberOfStars);

switch (new Intl.PluralRules(locale).select(numberOfStars)) {
case "one":
    console.log(`The rating is ${numberString} star`);
    break;
default:
    console.log(`The rating is ${numberString} stars`);
    break;
}

このコードはThe rating is 1.0 starになってしまいます。
シンプルなルールの英語ですら誤りであり、複雑な語形変化を持つ言語では、問題はさらに顕著になります。

Amountを使うと、コードは期待通りになり、流れも追いやすくなります。

let locale = "en-US";
let stars = new Amount(1, { fractionDigits: 1 });
let numberString = stars.toLocaleString(locale);

switch (stars.toLocalePlural(locale)) {
case "one":
    console.log(`The rating is ${numberString} star`);
    break;
default:
    console.log(`The rating is ${numberString} stars`);
    break;
}

Rounding

精度を下げると、値は四捨五入されます。

let a = new Amount("123.456");
a.with({ significantDigits: 5 }).toString(); // "123.46"

デフォルトでは、NumberやDecimalでも使用されているIEEE754で規定されたround-ties-to-evenが使用されます。
丸めモードを指定することも可能です。

let b = new Amount("123.456");
a.with({ significantDigits: 5, roundingMode: "truncate" }).toString(); // "123.45"

Units (including currency)

proposalの核となる機能のひとつは、単位と通貨のサポートです。
単位も通貨も必須ではありませんが、指定する場合は単位と通貨のいずれか片方だけを指定可能です。

let a = new Amount("123.456", { unit: "kg" }); // 123.456 kilograms
let b = new Amount("42.55", { unit: "EUR" });  // 42.55 Euros

単位の意味は規定されていないことに注意してください。
単位としてXYZkeelogramzなども使用可能です。
ただし、Intl.NumberFormatが対応していない単位でtoLocaleStringを呼びだすとエラーになります。

Related but out-of-scope features

スコープ外となった機能。
Amountは、小規模で容易に実現できる機能の核となることを目指しています。
以下の提案は、今後の拡張に含まれる可能性はありますが、現在はスコープ外です。

Mathematical operations

算術演算への対応。

Amount同士の四則演算、スカラー値との乗算・除算など。
gからkgなどへのスケール変換。

Unit conversion

マイルからメートルなどへの単位変換。

Derived units

メートルから平方メートルなどへの単位の派生。

Compound units

単位の組み合わせ。
身長を71inch5.92feetではなく5 feet 11 inchesと表す。

Polyfill

既にPolyfillが存在します。
本proposalはステージ1であるため、今後互換性のない変更が入る可能性があります。
本番環境での使用には適していません。

mathjs使うわ。

mathjs

1t + 2kg + 3g + 4mg; // 1.002003004 t




元の記事を確認する

関連記事