]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
[fix] Handle non-primitives in isNumber (#12034)
authorJosh Kelley <joshkel@gmail.com>
Sun, 16 Feb 2025 17:24:26 +0000 (12:24 -0500)
committerGitHub <noreply@github.com>
Sun, 16 Feb 2025 17:24:26 +0000 (12:24 -0500)
While investigating https://github.com/chartjs/chartjs-plugin-zoom/issues/928, I found that `isNonPrimitive` will throw TypeError on a Moment.js object after it's passed through Chart.js's options proxy, because the object has its `Symbol.toPrimitive`, `toString`, and `valueOf` all set to null.

(See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#string_coercion for background reading.)

Since isNumber appears to be a low-level function that can take any arbitrary input, it seems worth letting it handle this case.

src/helpers/helpers.math.ts
test/specs/helpers.math.tests.js

index da463aeb04ea0dd55a516d8c5bdf55e11f5ca49d..0fd2a95132caf6156735b0a09c13fa51ff48c33b 100644 (file)
@@ -57,8 +57,15 @@ export function _factorize(value: number) {
   return result;
 }
 
+/**
+ * Verifies that attempting to coerce n to string or number won't throw a TypeError.
+ */
+function isNonPrimitive(n: unknown) {
+  return typeof n === 'symbol' || (typeof n === 'object' && n !== null && !(Symbol.toPrimitive in n || 'toString' in n || 'valueOf' in n));
+}
+
 export function isNumber(n: unknown): n is number {
-  return !isNaN(parseFloat(n as string)) && isFinite(n as number);
+  return !isNonPrimitive(n) && !isNaN(parseFloat(n as string)) && isFinite(n as number);
 }
 
 export function almostWhole(x: number, epsilon: number) {
index b6b8e125f093ae934efa1e7b28e8ef9902ccdef3..938742959da74cd3061b700c4cd04a50d100851d 100644 (file)
@@ -103,6 +103,8 @@ describe('Chart.helpers.math', function() {
     expect(math.isNumber(NaN)).toBe(false);
     expect(math.isNumber(undefined)).toBe(false);
     expect(math.isNumber('cbc')).toBe(false);
+    expect(math.isNumber(Symbol())).toBe(false);
+    expect(math.isNumber(Object.create(null))).toBe(false);
   });
 
   it('should compute shortest distance between angles', function() {