From 496a62c0ae1be01297c77fd05ec6b4b433a713d5 Mon Sep 17 00:00:00 2001 From: Kai Gritun Date: Fri, 6 Feb 2026 22:20:24 -0500 Subject: [PATCH] fix: preserve NaN and Infinity values in typed arrays Fixes #292 Previously, NaN and Infinity values in typed arrays like Float64Array were incorrectly serialized as null by JSON.stringify, causing data loss during deserialization (NaN -> null -> 0). This change encodes special float values (NaN, Infinity, -Infinity) as strings within typed arrays during serialization and converts them back during deserialization, consistent with how these values are handled elsewhere in superjson. Added test case covering Float64Array and Float32Array with NaN and Infinity values. --- src/index.test.ts | 27 +++++++++++++++++++++++++++ src/transformer.ts | 20 ++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index daea0194..00917c97 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -649,6 +649,33 @@ describe('stringify & parse', () => { }, }, + 'works with NaN and Infinity in Float64Array, issue #292': { + input: { + a: new Float64Array([NaN, 0, NaN, 1]), + b: new Float64Array([Infinity, -Infinity, NaN, 0]), + c: new Float32Array([NaN, Infinity, -Infinity]), + }, + output: { + a: ['NaN', 0, 'NaN', 1], + b: ['Infinity', '-Infinity', 'NaN', 0], + c: ['NaN', 'Infinity', '-Infinity'], + }, + outputAnnotations: { + values: { + a: [['typed-array', 'Float64Array']], + b: [['typed-array', 'Float64Array']], + c: [['typed-array', 'Float32Array']], + }, + }, + customExpectations: (value: any) => { + expect(Number.isNaN(value.a[0])).toBe(true); + expect(Number.isNaN(value.a[2])).toBe(true); + expect(value.b[0]).toBe(Infinity); + expect(value.b[1]).toBe(-Infinity); + expect(Number.isNaN(value.b[2])).toBe(true); + }, + }, + 'works for undefined, issue #48': { input: undefined, output: null, diff --git a/src/transformer.ts b/src/transformer.ts index c48a015e..69dedef1 100644 --- a/src/transformer.ts +++ b/src/transformer.ts @@ -224,7 +224,15 @@ const constructorToName = [ const typedArrayRule = compositeTransformation( isTypedArray, v => ['typed-array', v.constructor.name], - v => [...v], + v => [...v].map(n => { + // Handle special float values that JSON.stringify converts to null + if (typeof n === 'number') { + if (Number.isNaN(n)) return 'NaN'; + if (n === Infinity) return 'Infinity'; + if (n === -Infinity) return '-Infinity'; + } + return n; + }), (v, a) => { const ctor = constructorToName[a[1]]; @@ -232,7 +240,15 @@ const typedArrayRule = compositeTransformation( throw new Error('Trying to deserialize unknown typed array'); } - return new ctor(v); + // Convert string representations back to special float values + const values = v.map((n: number | string): number => { + if (n === 'NaN') return NaN; + if (n === 'Infinity') return Infinity; + if (n === '-Infinity') return -Infinity; + return n as number; + }); + + return new ctor(values as number[]); } );