A TC39 proposal to add Math.clamp
: a function that constrains a value between an upper and lower bound.
Stage: 0
Champion: Oliver Medhurst (@canadahonk)
Authors: Oliver Medhurst (@canadahonk), Richie Bendall (@richienb)
Last Presented: (unpresented)
A clamping function constrains a value between an upper and lower bound.
Our primary motivation is its usefulness and popularity in existing projects where it is often defined for the sake of readability. A common use is for animations and interactive content. For example, it helps to keep objects in-bounds during user-controlled movement by restricting the coordinates that it can move to (see the p5.js demo for its constrain
function). Projects tend to define a function that looks like clamp(number, min, max)
, either through:
- Chaining mathematical operators with if statements or ternary operators
function clamp(number, minimum, maximum) {
if (number < minimum) {
return minimum;
}
if (number > maximum) {
return maximum;
}
return number;
}
function clamp(number, minimum, maximum) {
return Math.min(Math.max(number, minimum), maximum);
}
Each of those examples require unnecessary boilerplate and are error-prone. For example, a developer only has to mistype a single operator or mix up a single variable name for the function to break. They also disregard the potential undefined behaviour that can occur when minimum is larger than maximum, or when only min
or max
is specified.
We name it the function clamp
, like how it is in other programming languages...
clamp
in CSSMath.Clamp
in C#std::clamp
in the C++ standard libraryclamp
forf32
andf64
in RustcoerceIn
in Kotlinclamp
in Dartclamp
in Rubyclamp
in Elm
...and userland implementations:
Another motivation is to bring parity with the CSS function of the same name, although it will have a different parameter order because of the slightly different use cases in each context (see also the previous discussion on the order of options for CSS clamp
.
The original proposal intended to have the min
and max
arguments optional and allowing null
or undefined
as values to mean no upper/lower bound; but following recent TC39 requirements, it was agreed among some delegates that it would be best to not do this, especially since Math.min
/Math.max
remains available for uses with a single bound.
The proposed API allows a developer to clamp numbers like:
Math.clamp(5, 0, 10) // 5
Math.clamp(-5, 0, 10) // 0
Math.clamp(15, 0, 10) // 10
It supports -Infinity
/Infinity
to specify when there is no upper or lower bound, although Math.min
/Math.max
are also already available to use:
Math.clamp(5, 0, Infinity) === Math.max(5, 0) // 5
Math.clamp(-5, -Infinity, 10) === Math.min(-5, 10) // -5
If the minimum bound is larger than the maximum bound, it throws a RangeError
to avoid possible developer confusion:
Math.clamp(10, 5, 0) // RangeError
It also correctly respects -0
if given:
Math.clamp(-2, -0, 10) // -0
Math.clamp(-0, -0, 10) // -0
Math.clamp(0, -0, 10) // 0
Past work: