From 982dc2b7fb6bad27f69e9c05fa5adcc38b8b4060 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 18:45:09 +0000 Subject: [PATCH 1/2] Initial plan From 8e1094d11f614f859cd8f074229c393f46fadb05 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 18:49:29 +0000 Subject: [PATCH 2/2] Improve array function error messages to be user-friendly Updated error messages in src/functions/array/operations.ts to include: - Function signature information - The actual type that was received - Usage examples Added comprehensive tests for all updated error messages. Co-authored-by: Sander-Toonen <5106372+Sander-Toonen@users.noreply.github.com> --- src/functions/array/operations.ts | 94 +++++++++--- test/functions/functions-array-errors.ts | 175 +++++++++++++++++++++++ 2 files changed, 252 insertions(+), 17 deletions(-) create mode 100644 test/functions/functions-array-errors.ts diff --git a/src/functions/array/operations.ts b/src/functions/array/operations.ts index 57e3279..a90c852 100644 --- a/src/functions/array/operations.ts +++ b/src/functions/array/operations.ts @@ -3,15 +3,30 @@ * Handles array manipulation and processing operations */ +/** + * Get a user-friendly type name for a value + */ +function getTypeName(value: unknown): string { + if (value === null) return 'null'; + if (Array.isArray(value)) return 'array'; + return typeof value; +} + export function filter(f: Function, a: any[] | undefined): any[] | undefined { if (a === undefined) { return undefined; } if (typeof f !== 'function') { - throw new Error('First argument to filter is not a function'); + throw new Error( + `filter(predicate, array) expects a function as first argument, got ${getTypeName(f)}.\n` + + 'Example: filter(x => x > 0, [1, -2, 3])' + ); } if (!Array.isArray(a)) { - throw new Error('Second argument to filter is not an array'); + throw new Error( + `filter(predicate, array) expects an array as second argument, got ${getTypeName(a)}.\n` + + 'Example: filter(x => x > 0, [1, -2, 3])' + ); } return a.filter(function (x: any, i: number): any { return f(x, i); @@ -23,10 +38,16 @@ export function fold(f: Function, init: any, a: any[] | undefined): any { return undefined; } if (typeof f !== 'function') { - throw new Error('First argument to fold is not a function'); + throw new Error( + `fold(reducer, initial, array) expects a function as first argument, got ${getTypeName(f)}.\n` + + 'Example: fold((acc, x) => acc + x, 0, [1, 2, 3])' + ); } if (!Array.isArray(a)) { - throw new Error('Second argument to fold is not an array'); + throw new Error( + `fold(reducer, initial, array) expects an array as third argument, got ${getTypeName(a)}.\n` + + 'Example: fold((acc, x) => acc + x, 0, [1, 2, 3])' + ); } return a.reduce(function (acc: any, x: any, i: number): any { return f(acc, x, i); @@ -38,7 +59,10 @@ export function indexOf(target: any, s: string | any[] | undefined): number | un return undefined; } if (!(Array.isArray(s) || typeof s === 'string')) { - throw new Error('Second argument to indexOf is not a string or array'); + throw new Error( + `indexOf(target, arrayOrString) expects a string or array as second argument, got ${getTypeName(s)}.\n` + + 'Example: indexOf("b", ["a", "b", "c"]) or indexOf("o", "hello")' + ); } return s.indexOf(target); @@ -49,7 +73,10 @@ export function join(sep: string | undefined, a: any[] | undefined): string | un return undefined; } if (!Array.isArray(a)) { - throw new Error('Second argument to join is not an array'); + throw new Error( + `join(separator, array) expects an array as second argument, got ${getTypeName(a)}.\n` + + 'Example: join(", ", ["a", "b", "c"])' + ); } return a.join(sep); @@ -60,10 +87,16 @@ export function map(f: Function, a: any[] | undefined): any[] | undefined { return undefined; } if (typeof f !== 'function') { - throw new Error('First argument to map is not a function'); + throw new Error( + `map(mapper, array) expects a function as first argument, got ${getTypeName(f)}.\n` + + 'Example: map(x => x * 2, [1, 2, 3])' + ); } if (!Array.isArray(a)) { - throw new Error('Second argument to map is not an array'); + throw new Error( + `map(mapper, array) expects an array as second argument, got ${getTypeName(a)}.\n` + + 'Example: map(x => x * 2, [1, 2, 3])' + ); } return a.map(function (x: any, i: number): any { return f(x, i); @@ -75,7 +108,10 @@ export function sum(array: (number | undefined)[] | undefined): number | undefin return undefined; } if (!Array.isArray(array)) { - throw new Error('Sum argument is not an array'); + throw new Error( + `sum(array) expects an array as argument, got ${getTypeName(array)}.\n` + + 'Example: sum([1, 2, 3, 4])' + ); } if (array.includes(undefined)) { return undefined; @@ -91,7 +127,10 @@ export function count(array: any[] | undefined): number | undefined { return undefined; } if (!Array.isArray(array)) { - throw new Error('Count argument is not an array'); + throw new Error( + `count(array) expects an array as argument, got ${getTypeName(array)}.\n` + + 'Example: count([1, 2, 3, 4])' + ); } return array.length; } @@ -106,10 +145,16 @@ export function find(f: Function, a: any[] | undefined): any { return undefined; } if (typeof f !== 'function') { - throw new Error('First argument to find is not a function'); + throw new Error( + `find(predicate, array) expects a function as first argument, got ${getTypeName(f)}.\n` + + 'Example: find(x => x > 2, [1, 2, 3, 4])' + ); } if (!Array.isArray(a)) { - throw new Error('Second argument to find is not an array'); + throw new Error( + `find(predicate, array) expects an array as second argument, got ${getTypeName(a)}.\n` + + 'Example: find(x => x > 2, [1, 2, 3, 4])' + ); } return a.find(function (x: any, i: number): any { return f(x, i); @@ -121,10 +166,16 @@ export function some(f: Function, a: any[] | undefined): boolean | undefined { return undefined; } if (typeof f !== 'function') { - throw new Error('First argument to some is not a function'); + throw new Error( + `some(predicate, array) expects a function as first argument, got ${getTypeName(f)}.\n` + + 'Example: some(x => x > 2, [1, 2, 3, 4])' + ); } if (!Array.isArray(a)) { - throw new Error('Second argument to some is not an array'); + throw new Error( + `some(predicate, array) expects an array as second argument, got ${getTypeName(a)}.\n` + + 'Example: some(x => x > 2, [1, 2, 3, 4])' + ); } return a.some(function (x: any, i: number): any { return f(x, i); @@ -136,10 +187,16 @@ export function every(f: Function, a: any[] | undefined): boolean | undefined { return undefined; } if (typeof f !== 'function') { - throw new Error('First argument to every is not a function'); + throw new Error( + `every(predicate, array) expects a function as first argument, got ${getTypeName(f)}.\n` + + 'Example: every(x => x > 0, [1, 2, 3, 4])' + ); } if (!Array.isArray(a)) { - throw new Error('Second argument to every is not an array'); + throw new Error( + `every(predicate, array) expects an array as second argument, got ${getTypeName(a)}.\n` + + 'Example: every(x => x > 0, [1, 2, 3, 4])' + ); } return a.every(function (x: any, i: number): any { return f(x, i); @@ -151,7 +208,10 @@ export function unique(a: any[] | undefined): any[] | undefined { return undefined; } if (!Array.isArray(a)) { - throw new Error('Argument to unique is not an array'); + throw new Error( + `unique(array) expects an array as argument, got ${getTypeName(a)}.\n` + + 'Example: unique([1, 2, 2, 3, 3, 3])' + ); } // Use Set to remove duplicates, then convert back to array return Array.from(new Set(a)); diff --git a/test/functions/functions-array-errors.ts b/test/functions/functions-array-errors.ts new file mode 100644 index 0000000..e520548 --- /dev/null +++ b/test/functions/functions-array-errors.ts @@ -0,0 +1,175 @@ +/* global describe, it */ + +import { Parser } from '../../index'; +import { expect } from 'vitest'; + +describe('Array Function Error Messages', function () { + describe('filter()', function () { + it('should provide user-friendly error when first argument is not a function', function () { + const parser = new Parser(); + expect(() => parser.evaluate('filter(42, [1, 2, 3])')).toThrow( + /filter\(predicate, array\) expects a function as first argument, got number/ + ); + expect(() => parser.evaluate('filter(42, [1, 2, 3])')).toThrow(/Example:/); + }); + + it('should provide user-friendly error when second argument is not an array', function () { + const parser = new Parser(); + expect(() => parser.evaluate('f(x) = x > 0; filter(f, "not an array")')).toThrow( + /filter\(predicate, array\) expects an array as second argument, got string/ + ); + expect(() => parser.evaluate('f(x) = x > 0; filter(f, "not an array")')).toThrow(/Example:/); + }); + }); + + describe('map()', function () { + it('should provide user-friendly error when first argument is not a function', function () { + const parser = new Parser(); + expect(() => parser.evaluate('map("not a function", [1, 2, 3])')).toThrow( + /map\(mapper, array\) expects a function as first argument, got string/ + ); + expect(() => parser.evaluate('map("not a function", [1, 2, 3])')).toThrow(/Example:/); + }); + + it('should provide user-friendly error when second argument is not an array', function () { + const parser = new Parser(); + expect(() => parser.evaluate('f(x) = x * 2; map(f, 123)')).toThrow( + /map\(mapper, array\) expects an array as second argument, got number/ + ); + }); + }); + + describe('fold()', function () { + it('should provide user-friendly error when first argument is not a function', function () { + const parser = new Parser(); + expect(() => parser.evaluate('fold(null, 0, [1, 2, 3])', { null: null })).toThrow( + /fold\(reducer, initial, array\) expects a function as first argument, got null/ + ); + expect(() => parser.evaluate('fold(null, 0, [1, 2, 3])', { null: null })).toThrow(/Example:/); + }); + + it('should provide user-friendly error when third argument is not an array', function () { + const parser = new Parser(); + expect(() => parser.evaluate('f(a, b) = a + b; fold(f, 0, {a: 1})')).toThrow( + /fold\(reducer, initial, array\) expects an array as third argument, got object/ + ); + }); + }); + + describe('reduce()', function () { + it('should provide user-friendly error from fold when first argument is not a function', function () { + const parser = new Parser(); + expect(() => parser.evaluate('reduce(true, 0, [1, 2, 3])')).toThrow( + /fold\(reducer, initial, array\) expects a function as first argument, got boolean/ + ); + }); + }); + + describe('find()', function () { + it('should provide user-friendly error when first argument is not a function', function () { + const parser = new Parser(); + expect(() => parser.evaluate('find([1, 2], [1, 2, 3])')).toThrow( + /find\(predicate, array\) expects a function as first argument, got array/ + ); + expect(() => parser.evaluate('find([1, 2], [1, 2, 3])')).toThrow(/Example:/); + }); + + it('should provide user-friendly error when second argument is not an array', function () { + const parser = new Parser(); + expect(() => parser.evaluate('f(x) = x > 0; find(f, 99)')).toThrow( + /find\(predicate, array\) expects an array as second argument, got number/ + ); + }); + }); + + describe('some()', function () { + it('should provide user-friendly error when first argument is not a function', function () { + const parser = new Parser(); + expect(() => parser.evaluate('some(5, [1, 2, 3])')).toThrow( + /some\(predicate, array\) expects a function as first argument, got number/ + ); + }); + + it('should provide user-friendly error when second argument is not an array', function () { + const parser = new Parser(); + expect(() => parser.evaluate('f(x) = x > 0; some(f, "string")')).toThrow( + /some\(predicate, array\) expects an array as second argument, got string/ + ); + }); + }); + + describe('every()', function () { + it('should provide user-friendly error when first argument is not a function', function () { + const parser = new Parser(); + expect(() => parser.evaluate('every({a: 1}, [1, 2, 3])')).toThrow( + /every\(predicate, array\) expects a function as first argument, got object/ + ); + }); + + it('should provide user-friendly error when second argument is not an array', function () { + const parser = new Parser(); + expect(() => parser.evaluate('f(x) = x > 0; every(f, false)')).toThrow( + /every\(predicate, array\) expects an array as second argument, got boolean/ + ); + }); + }); + + describe('indexOf()', function () { + it('should provide user-friendly error when second argument is not an array or string', function () { + const parser = new Parser(); + expect(() => parser.evaluate('indexOf(1, 123)')).toThrow( + /indexOf\(target, arrayOrString\) expects a string or array as second argument, got number/ + ); + expect(() => parser.evaluate('indexOf(1, 123)')).toThrow(/Example:/); + }); + }); + + describe('join()', function () { + it('should provide user-friendly error when second argument is not an array', function () { + const parser = new Parser(); + expect(() => parser.evaluate('join(", ", "not array")')).toThrow( + /join\(separator, array\) expects an array as second argument, got string/ + ); + expect(() => parser.evaluate('join(", ", "not array")')).toThrow(/Example:/); + }); + }); + + describe('sum()', function () { + it('should provide user-friendly error when argument is not an array', function () { + const parser = new Parser(); + expect(() => parser.evaluate('sum(42)')).toThrow( + /sum\(array\) expects an array as argument, got number/ + ); + expect(() => parser.evaluate('sum(42)')).toThrow(/Example:/); + }); + }); + + describe('count()', function () { + it('should provide user-friendly error when argument is not an array', function () { + const parser = new Parser(); + expect(() => parser.evaluate('count("string")')).toThrow( + /count\(array\) expects an array as argument, got string/ + ); + expect(() => parser.evaluate('count("string")')).toThrow(/Example:/); + }); + }); + + describe('unique()', function () { + it('should provide user-friendly error when argument is not an array', function () { + const parser = new Parser(); + expect(() => parser.evaluate('unique(123)')).toThrow( + /unique\(array\) expects an array as argument, got number/ + ); + expect(() => parser.evaluate('unique(123)')).toThrow(/Example:/); + }); + }); + + describe('distinct()', function () { + it('should provide user-friendly error from unique when argument is not an array', function () { + const parser = new Parser(); + expect(() => parser.evaluate('distinct({a: 1})')).toThrow( + /unique\(array\) expects an array as argument, got object/ + ); + }); + }); +});