Skip to content

Commit f903d62

Browse files
committed
extract Math.clamp to a separate proposal
1 parent bd84ac5 commit f903d62

File tree

10 files changed

+79
-28
lines changed

10 files changed

+79
-28
lines changed

β€ŽCHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
- `DataView.prototype.setFloat16`
1313
- Moved to stable ES, [February 2025 TC39 meeting](https://github.com/tc39/proposals/commit/b81fa9bccf4b51f33de0cbe797976a84d05d4b76)
1414
- Added `es.` namespace modules, `/es/` and `/stable/` namespaces entries
15+
- [`Math.clamp` stage 1 proposal](https://github.com/CanadaHonk/proposal-math-clamp):
16+
- Built-ins:
17+
- `Math.clamp`
18+
- Extracted from [old `Math` extensions proposal](https://github.com/rwaldron/proposal-math-extensions)
19+
- Added arguments validation
20+
- Added new entries
1521
- Compat data improvements:
1622
- [`DisposableStack`, `AsyncDisposableStack`, `SuppressedError` and `Iterator.prototype[@@dispose]`](https://github.com/tc39/proposal-explicit-resource-management) marked as [shipped from V8 ~ Chromium 134](https://issues.chromium.org/issues/42203506#comment24)
1723
- [`Error.isError`](https://github.com/tc39/proposal-is-error) added and marked as supported from V8 ~ Chromium 134

β€ŽREADME.md

+20
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ structuredClone(new Set([1, 2, 3])); // => new Set([1, 2, 3])
186186
- [`Array` filtering](#array-filtering)
187187
- [`Array` deduplication](#array-deduplication)
188188
- [`DataView` get / set `Uint8Clamped` methods](#dataview-get-set-iint8clamped-methods)
189+
- [`Math.clamp`](#mathclamp)
189190
- [`Number.fromString`](#numberfromstring)
190191
- [`String.cooked`](#stringcooked)
191192
- [`String.prototype.codePoints`](#stringprototypecodepoints)
@@ -3154,6 +3155,25 @@ view.setUint8Clamped(0, 100500);
31543155
console.log(view.getUint8Clamped(0)); // => 255
31553156
```
31563157

3158+
##### [`Math.clamp`](https://github.com/CanadaHonk/proposal-math-clamp)[⬆](#index)
3159+
Module [`esnext.math.clamp`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.math.clamp.js)
3160+
```ts
3161+
class Math {
3162+
clamp(value: number, min: number, max: number): number;
3163+
}
3164+
```
3165+
[*CommonJS entry points:*](#commonjs-api)
3166+
```
3167+
core-js/proposals/math-clamp
3168+
core-js(-pure)/full/math/clamp
3169+
```
3170+
[*Example*](https://tinyurl.com/2ayyqr8j):
3171+
```js
3172+
Math.clamp(5, 0, 10); // => 5
3173+
Math.clamp(-5, 0, 10); // => 0
3174+
Math.clamp(15, 0, 10); // => 10
3175+
````
3176+
31573177
##### [`Number.fromString`](https://github.com/tc39/proposal-number-fromstring)[⬆](#index)
31583178
Module [`esnext.number.from-string`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.number.from-string.js)
31593179
```ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
var $TypeError = TypeError;
3+
4+
module.exports = function (argument) {
5+
if (typeof argument == 'number') return argument;
6+
throw new $TypeError('Argument is not a number');
7+
};
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
'use strict';
22
var $ = require('../internals/export');
3+
var aNumber = require('../internals/a-number');
4+
var notANaN = require('../internals/not-a-nan');
5+
var sameValue = require('../internals/same-value');
36

4-
var min = Math.min;
5-
var max = Math.max;
7+
var $RangeError = RangeError;
8+
var $min = Math.min;
9+
var $max = Math.max;
610

711
// `Math.clamp` method
812
// https://rwaldron.github.io/proposal-math-extensions/
913
$({ target: 'Math', stat: true, forced: true }, {
10-
clamp: function clamp(x, lower, upper) {
11-
return min(upper, max(lower, x));
14+
clamp: function clamp(value, min, max) {
15+
aNumber(value);
16+
notANaN(aNumber(min));
17+
notANaN(aNumber(max));
18+
if ((sameValue(min, 0) && sameValue(max, -0)) || min > max) throw new $RangeError('`min` should be smaller than `max`');
19+
return $min(max, $max(min, value));
1220
}
1321
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict';
2+
// https://github.com/CanadaHonk/proposal-math-clamp
3+
require('../modules/esnext.math.clamp');

β€Žpackages/core-js/stage/1.js

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require('../proposals/collection-methods');
88
require('../proposals/collection-of-from');
99
require('../proposals/data-view-get-set-uint8-clamped');
1010
require('../proposals/keys-composition');
11+
require('../proposals/math-clamp');
1112
require('../proposals/math-extensions');
1213
require('../proposals/math-signbit');
1314
require('../proposals/number-from-string');

β€Žtests/codespell/runner.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const skip = [
77
];
88

99
const ignoreWords = [
10+
'aNumber',
1011
'larg',
1112
'outLow',
1213
'statics',

β€Žtests/entries/unit.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,7 @@ for (PATH of ['core-js-pure', 'core-js']) {
946946
load('proposals/map-upsert');
947947
load('proposals/map-upsert-stage-2');
948948
load('proposals/map-upsert-v4');
949+
load('proposals/math-clamp');
949950
load('proposals/math-extensions');
950951
load('proposals/math-signbit');
951952
load('proposals/math-sum');
+14-12
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
1-
import { createConversionChecker } from '../helpers/helpers.js';
2-
31
QUnit.test('Math.clamp', assert => {
42
const { clamp } = Math;
53
assert.isFunction(clamp);
64
assert.name(clamp, 'clamp');
75
assert.arity(clamp, 3);
86
assert.looksNative(clamp);
97
assert.nonEnumerable(Math, 'clamp');
8+
109
assert.same(clamp(2, 4, 6), 4);
1110
assert.same(clamp(4, 2, 6), 4);
1211
assert.same(clamp(6, 2, 4), 4);
1312

14-
const checker1 = createConversionChecker(2);
15-
const checker2 = createConversionChecker(4);
16-
const checker3 = createConversionChecker(5);
17-
assert.same(clamp(checker1, checker2, checker3), 4, 'object wrapper');
18-
assert.same(checker1.$valueOf, 1, 'checker1 valueOf calls');
19-
assert.same(checker1.$toString, 0, 'checker1 toString calls');
20-
assert.same(checker2.$valueOf, 1, 'checker2 valueOf calls');
21-
assert.same(checker2.$toString, 0, 'checker2 toString calls');
22-
assert.same(checker3.$valueOf, 1, 'checker3 valueOf calls');
23-
assert.same(checker3.$toString, 0, 'checker3 toString calls');
13+
assert.same(clamp(NaN, 4, 6), NaN, 'If value is NaN, return NaN.');
14+
assert.same(clamp(-0, 0, 1), 0, 'If value is -0𝔽 and min is +0𝔽, return +0𝔽.');
15+
assert.same(clamp(0, -0, 1), 0, 'If value is +0𝔽 and min is -0𝔽, return +0𝔽.');
16+
assert.same(clamp(-0, -1, 0), -0, 'If value is -0𝔽 and max is +0𝔽, return -0𝔽.');
17+
assert.same(clamp(0, -1, -0), -0, 'If value is +0𝔽 and max is -0𝔽, return -0𝔽.');
18+
19+
assert.throws(() => clamp(Object(2), 1, 3), TypeError, 'If value is not a Number, throw a TypeError exception.');
20+
assert.throws(() => clamp(2, Object(1), 3), TypeError, 'If min is not a Number, throw a TypeError exception.');
21+
assert.throws(() => clamp(2, NaN, 3), RangeError, 'If min is NaN, throw a RangeError exception.');
22+
assert.throws(() => clamp(2, 1, Object(3)), TypeError, 'If max is not a Number, throw a TypeError exception.');
23+
assert.throws(() => clamp(2, 1, NaN), RangeError, 'If max is NaN, throw a RangeError exception.');
24+
assert.throws(() => clamp(2, 0, -0), RangeError, 'If min is +0𝔽 and max is -0𝔽, throw a RangeError exception.');
25+
assert.throws(() => clamp(2, 3, 1), RangeError, 'If min > max, throw a RangeError exception.');
2426
});
+14-12
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
1-
import { createConversionChecker } from '../helpers/helpers.js';
2-
31
import clamp from 'core-js-pure/full/math/clamp';
42

53
QUnit.test('Math.clamp', assert => {
64
assert.isFunction(clamp);
75
assert.arity(clamp, 3);
6+
87
assert.same(clamp(2, 4, 6), 4);
98
assert.same(clamp(4, 2, 6), 4);
109
assert.same(clamp(6, 2, 4), 4);
1110

12-
const checker1 = createConversionChecker(2);
13-
const checker2 = createConversionChecker(4);
14-
const checker3 = createConversionChecker(5);
15-
assert.same(clamp(checker1, checker2, checker3), 4, 'object wrapper');
16-
assert.same(checker1.$valueOf, 1, 'checker1 valueOf calls');
17-
assert.same(checker1.$toString, 0, 'checker1 toString calls');
18-
assert.same(checker2.$valueOf, 1, 'checker2 valueOf calls');
19-
assert.same(checker2.$toString, 0, 'checker2 toString calls');
20-
assert.same(checker3.$valueOf, 1, 'checker3 valueOf calls');
21-
assert.same(checker3.$toString, 0, 'checker3 toString calls');
11+
assert.same(clamp(NaN, 4, 6), NaN, 'If value is NaN, return NaN.');
12+
assert.same(clamp(-0, 0, 1), 0, 'If value is -0𝔽 and min is +0𝔽, return +0𝔽.');
13+
assert.same(clamp(0, -0, 1), 0, 'If value is +0𝔽 and min is -0𝔽, return +0𝔽.');
14+
assert.same(clamp(-0, -1, 0), -0, 'If value is -0𝔽 and max is +0𝔽, return -0𝔽.');
15+
assert.same(clamp(0, -1, -0), -0, 'If value is +0𝔽 and max is -0𝔽, return -0𝔽.');
16+
17+
assert.throws(() => clamp(Object(2), 1, 3), TypeError, 'If value is not a Number, throw a TypeError exception.');
18+
assert.throws(() => clamp(2, Object(1), 3), TypeError, 'If min is not a Number, throw a TypeError exception.');
19+
assert.throws(() => clamp(2, NaN, 3), RangeError, 'If min is NaN, throw a RangeError exception.');
20+
assert.throws(() => clamp(2, 1, Object(3)), TypeError, 'If max is not a Number, throw a TypeError exception.');
21+
assert.throws(() => clamp(2, 1, NaN), RangeError, 'If max is NaN, throw a RangeError exception.');
22+
assert.throws(() => clamp(2, 0, -0), RangeError, 'If min is +0𝔽 and max is -0𝔽, throw a RangeError exception.');
23+
assert.throws(() => clamp(2, 3, 1), RangeError, 'If min > max, throw a RangeError exception.');
2224
});

0 commit comments

Comments
Β (0)