diff --git a/src/options.js b/src/options.js index aa09100073..1bd8b88b9e 100644 --- a/src/options.js +++ b/src/options.js @@ -14,7 +14,7 @@ export function valueof(data, value, type) { ? maybeTypedMap(data, value, type) : valueType === "number" || value instanceof Date || valueType === "boolean" ? map(data, constant(value), type) - : value && typeof value.transform === "function" + : typeof value?.transform === "function" ? maybeTypedArrayify(value.transform(data), type) : maybeTypedArrayify(value, type); } @@ -128,7 +128,7 @@ export function arrayify(data) { // An optimization of type.from(values, f): if the given values are already an // instanceof the desired array type, the faster values.map method is used. export function map(values, f, type = Array) { - return values instanceof type ? values.map(f) : type.from(values, f); + return values == null ? values : values instanceof type ? values.map(f) : type.from(values, f); } // An optimization of type.from(values): if the given values are already an diff --git a/test/options-d-test.ts b/test/options-d-test.ts new file mode 100644 index 0000000000..1c905da9b2 --- /dev/null +++ b/test/options-d-test.ts @@ -0,0 +1,21 @@ +import {valueof} from "../src/options.js"; + +// A function is not a valid input data +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error +valueof(() => {}, "field"); + +// A Promise is not a valid input data +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error +valueof(new Promise(() => {}), "field"); + +// A symbol is not a valid value +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error +valueof(null, Symbol("field")); + +// A bigint is not a valid value +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error +valueof(null, 2n); diff --git a/test/options-test.js b/test/options-test.js index 4778494298..4b96b06372 100644 --- a/test/options-test.js +++ b/test/options-test.js @@ -114,3 +114,28 @@ it("valueof returns arrays that match the specified type as-is", () => { assert.strictEqual(valueof(m, identity, Float32Array), m); assert.strictEqual(valueof(m, identity, My32Array), m); }); + +it("valueof returns the given array value", () => { + const a = [1, 2, 3]; + assert.strictEqual(valueof(null, a), a); + assert.strictEqual(valueof(undefined, a), a); + assert.strictEqual(valueof([], a), a); +}); + +it("valueof accepts complicated data with the proper accessor", () => { + const m = [(d) => d, new Promise(() => {})]; + assert.deepStrictEqual(valueof(m, String), ["(d) => d", "[object Promise]"]); +}); + +it("data passed to valueof can be nullish and generated by the transform method", () => { + assert.deepStrictEqual(valueof(undefined, {transform: () => [1, "text"]}), [1, "text"]); + assert.deepStrictEqual(valueof(null, {transform: () => [1, "text"]}, Float32Array), Float32Array.of(1, NaN)); + assert.deepStrictEqual(valueof(null, {transform: () => new Float64Array(2)}, Array), [0, 0]); +}); + +it("valueof does not crash on nullish data with an accessor", () => { + const a = () => 1; + assert.strictEqual(valueof(null, a), null); + assert.strictEqual(valueof(undefined, a), undefined); + assert.deepStrictEqual(valueof(0, a), []); // ill-defined, but not crashing +});