We already have template literals and an extensible Number.prototype. The value prop here seems a bit low for a syntactic change.
This works today:
const unit = name => {
const f = (pieces, ...cooked) => ({
[f]:
Array.isArray(pieces)
? String.raw(pieces, ...cooked)
: String(pieces),
})
const sym = Symbol(name)
f[Symbol.toPrimitive] = () => sym
Object.defineProperty(Number.prototype, f, {get() { return f(this) }})
Object.defineProperty(String.prototype, f, {get() { return f(this) }})
return f
}
const m = unit('meters')
10[m] // {Symbol(meters): "10"}
m`42` // {Symbol(meters): "42"}
'99'[m] // {Symbol(meters): "99"}
// And of course it works on variables
const len = 20
len[m] // {Symbol(meters): "20"}
// Expressions
(2 + 3)[m] // {Symbol(meters): "5"}
// And it's a function, so we can do function things with it.
[20, 40, 50].map(m)
// [ {Symbol(meters): "20"}, {Symbol(meters): "40"}, {Symbol(meters): "50"} ]
Yes, extending global prototypes is not ideal, but doing it with Symbols is very safe—I can't see any problems with the above. Even if we have multiple versions of the same library trying to walk all over each other, they won't be able to.
These are both interesting ideas, and we might want to make the tradeoff of encouraging those idioms rather than introducing a new language feature. Here are some considerations that led to this proposal:
Disadvantages of both possibilities
One goal was to have syntax which matched BigInt, in a way which is analogous to various numeric types in other languages (a short character suffix at the end of a numberic literal).
It's a couple more ugly characters! (OK, this is a weak argument.)
Disadvantages of methods on Number.prototype:
We don't actually want to encourage it to be used as a function or to operate on a Number--this could lead to a loss of precision, in the case of things like BigInt or Decimal (where the Number just can't represent everything). This is a major disadvantage of the methods-on-Number.prototype solution.
This proposal attempts to be based on lexical scoping, rather than depending on global mutable objects like Number.prototype. This way, it can continue to work in a world where a lot of things are frozen.
Disadvantages of template literals:
Extensible literal suffixes give fast access (without even using a WeakMap) to the thing pre-parsed as Number, which template literals don't give.
Runtime error, rather than parse-time error, for an invalid numeric literal.
We already have template literals and an extensible
Number.prototype
. The value prop here seems a bit low for a syntactic change.This works today:
Yes, extending global prototypes is not ideal, but doing it with Symbols is very safe—I can't see any problems with the above. Even if we have multiple versions of the same library trying to walk all over each other, they won't be able to.
Are there use cases I'm missing?