Skip to content

Commit c09c377

Browse files
authored
Improve array function error messages with signatures and examples (#35)
* 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.
1 parent 9d46225 commit c09c377

2 files changed

Lines changed: 252 additions & 17 deletions

File tree

src/functions/array/operations.ts

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,30 @@
33
* Handles array manipulation and processing operations
44
*/
55

6+
/**
7+
* Get a user-friendly type name for a value
8+
*/
9+
function getTypeName(value: unknown): string {
10+
if (value === null) return 'null';
11+
if (Array.isArray(value)) return 'array';
12+
return typeof value;
13+
}
14+
615
export function filter(f: Function, a: any[] | undefined): any[] | undefined {
716
if (a === undefined) {
817
return undefined;
918
}
1019
if (typeof f !== 'function') {
11-
throw new Error('First argument to filter is not a function');
20+
throw new Error(
21+
`filter(predicate, array) expects a function as first argument, got ${getTypeName(f)}.\n` +
22+
'Example: filter(x => x > 0, [1, -2, 3])'
23+
);
1224
}
1325
if (!Array.isArray(a)) {
14-
throw new Error('Second argument to filter is not an array');
26+
throw new Error(
27+
`filter(predicate, array) expects an array as second argument, got ${getTypeName(a)}.\n` +
28+
'Example: filter(x => x > 0, [1, -2, 3])'
29+
);
1530
}
1631
return a.filter(function (x: any, i: number): any {
1732
return f(x, i);
@@ -23,10 +38,16 @@ export function fold(f: Function, init: any, a: any[] | undefined): any {
2338
return undefined;
2439
}
2540
if (typeof f !== 'function') {
26-
throw new Error('First argument to fold is not a function');
41+
throw new Error(
42+
`fold(reducer, initial, array) expects a function as first argument, got ${getTypeName(f)}.\n` +
43+
'Example: fold((acc, x) => acc + x, 0, [1, 2, 3])'
44+
);
2745
}
2846
if (!Array.isArray(a)) {
29-
throw new Error('Second argument to fold is not an array');
47+
throw new Error(
48+
`fold(reducer, initial, array) expects an array as third argument, got ${getTypeName(a)}.\n` +
49+
'Example: fold((acc, x) => acc + x, 0, [1, 2, 3])'
50+
);
3051
}
3152
return a.reduce(function (acc: any, x: any, i: number): any {
3253
return f(acc, x, i);
@@ -38,7 +59,10 @@ export function indexOf(target: any, s: string | any[] | undefined): number | un
3859
return undefined;
3960
}
4061
if (!(Array.isArray(s) || typeof s === 'string')) {
41-
throw new Error('Second argument to indexOf is not a string or array');
62+
throw new Error(
63+
`indexOf(target, arrayOrString) expects a string or array as second argument, got ${getTypeName(s)}.\n` +
64+
'Example: indexOf("b", ["a", "b", "c"]) or indexOf("o", "hello")'
65+
);
4266
}
4367

4468
return s.indexOf(target);
@@ -49,7 +73,10 @@ export function join(sep: string | undefined, a: any[] | undefined): string | un
4973
return undefined;
5074
}
5175
if (!Array.isArray(a)) {
52-
throw new Error('Second argument to join is not an array');
76+
throw new Error(
77+
`join(separator, array) expects an array as second argument, got ${getTypeName(a)}.\n` +
78+
'Example: join(", ", ["a", "b", "c"])'
79+
);
5380
}
5481

5582
return a.join(sep);
@@ -60,10 +87,16 @@ export function map(f: Function, a: any[] | undefined): any[] | undefined {
6087
return undefined;
6188
}
6289
if (typeof f !== 'function') {
63-
throw new Error('First argument to map is not a function');
90+
throw new Error(
91+
`map(mapper, array) expects a function as first argument, got ${getTypeName(f)}.\n` +
92+
'Example: map(x => x * 2, [1, 2, 3])'
93+
);
6494
}
6595
if (!Array.isArray(a)) {
66-
throw new Error('Second argument to map is not an array');
96+
throw new Error(
97+
`map(mapper, array) expects an array as second argument, got ${getTypeName(a)}.\n` +
98+
'Example: map(x => x * 2, [1, 2, 3])'
99+
);
67100
}
68101
return a.map(function (x: any, i: number): any {
69102
return f(x, i);
@@ -75,7 +108,10 @@ export function sum(array: (number | undefined)[] | undefined): number | undefin
75108
return undefined;
76109
}
77110
if (!Array.isArray(array)) {
78-
throw new Error('Sum argument is not an array');
111+
throw new Error(
112+
`sum(array) expects an array as argument, got ${getTypeName(array)}.\n` +
113+
'Example: sum([1, 2, 3, 4])'
114+
);
79115
}
80116
if (array.includes(undefined)) {
81117
return undefined;
@@ -91,7 +127,10 @@ export function count(array: any[] | undefined): number | undefined {
91127
return undefined;
92128
}
93129
if (!Array.isArray(array)) {
94-
throw new Error('Count argument is not an array');
130+
throw new Error(
131+
`count(array) expects an array as argument, got ${getTypeName(array)}.\n` +
132+
'Example: count([1, 2, 3, 4])'
133+
);
95134
}
96135
return array.length;
97136
}
@@ -106,10 +145,16 @@ export function find(f: Function, a: any[] | undefined): any {
106145
return undefined;
107146
}
108147
if (typeof f !== 'function') {
109-
throw new Error('First argument to find is not a function');
148+
throw new Error(
149+
`find(predicate, array) expects a function as first argument, got ${getTypeName(f)}.\n` +
150+
'Example: find(x => x > 2, [1, 2, 3, 4])'
151+
);
110152
}
111153
if (!Array.isArray(a)) {
112-
throw new Error('Second argument to find is not an array');
154+
throw new Error(
155+
`find(predicate, array) expects an array as second argument, got ${getTypeName(a)}.\n` +
156+
'Example: find(x => x > 2, [1, 2, 3, 4])'
157+
);
113158
}
114159
return a.find(function (x: any, i: number): any {
115160
return f(x, i);
@@ -121,10 +166,16 @@ export function some(f: Function, a: any[] | undefined): boolean | undefined {
121166
return undefined;
122167
}
123168
if (typeof f !== 'function') {
124-
throw new Error('First argument to some is not a function');
169+
throw new Error(
170+
`some(predicate, array) expects a function as first argument, got ${getTypeName(f)}.\n` +
171+
'Example: some(x => x > 2, [1, 2, 3, 4])'
172+
);
125173
}
126174
if (!Array.isArray(a)) {
127-
throw new Error('Second argument to some is not an array');
175+
throw new Error(
176+
`some(predicate, array) expects an array as second argument, got ${getTypeName(a)}.\n` +
177+
'Example: some(x => x > 2, [1, 2, 3, 4])'
178+
);
128179
}
129180
return a.some(function (x: any, i: number): any {
130181
return f(x, i);
@@ -136,10 +187,16 @@ export function every(f: Function, a: any[] | undefined): boolean | undefined {
136187
return undefined;
137188
}
138189
if (typeof f !== 'function') {
139-
throw new Error('First argument to every is not a function');
190+
throw new Error(
191+
`every(predicate, array) expects a function as first argument, got ${getTypeName(f)}.\n` +
192+
'Example: every(x => x > 0, [1, 2, 3, 4])'
193+
);
140194
}
141195
if (!Array.isArray(a)) {
142-
throw new Error('Second argument to every is not an array');
196+
throw new Error(
197+
`every(predicate, array) expects an array as second argument, got ${getTypeName(a)}.\n` +
198+
'Example: every(x => x > 0, [1, 2, 3, 4])'
199+
);
143200
}
144201
return a.every(function (x: any, i: number): any {
145202
return f(x, i);
@@ -151,7 +208,10 @@ export function unique(a: any[] | undefined): any[] | undefined {
151208
return undefined;
152209
}
153210
if (!Array.isArray(a)) {
154-
throw new Error('Argument to unique is not an array');
211+
throw new Error(
212+
`unique(array) expects an array as argument, got ${getTypeName(a)}.\n` +
213+
'Example: unique([1, 2, 2, 3, 3, 3])'
214+
);
155215
}
156216
// Use Set to remove duplicates, then convert back to array
157217
return Array.from(new Set(a));
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/* global describe, it */
2+
3+
import { Parser } from '../../index';
4+
import { expect } from 'vitest';
5+
6+
describe('Array Function Error Messages', function () {
7+
describe('filter()', function () {
8+
it('should provide user-friendly error when first argument is not a function', function () {
9+
const parser = new Parser();
10+
expect(() => parser.evaluate('filter(42, [1, 2, 3])')).toThrow(
11+
/filter\(predicate, array\) expects a function as first argument, got number/
12+
);
13+
expect(() => parser.evaluate('filter(42, [1, 2, 3])')).toThrow(/Example:/);
14+
});
15+
16+
it('should provide user-friendly error when second argument is not an array', function () {
17+
const parser = new Parser();
18+
expect(() => parser.evaluate('f(x) = x > 0; filter(f, "not an array")')).toThrow(
19+
/filter\(predicate, array\) expects an array as second argument, got string/
20+
);
21+
expect(() => parser.evaluate('f(x) = x > 0; filter(f, "not an array")')).toThrow(/Example:/);
22+
});
23+
});
24+
25+
describe('map()', function () {
26+
it('should provide user-friendly error when first argument is not a function', function () {
27+
const parser = new Parser();
28+
expect(() => parser.evaluate('map("not a function", [1, 2, 3])')).toThrow(
29+
/map\(mapper, array\) expects a function as first argument, got string/
30+
);
31+
expect(() => parser.evaluate('map("not a function", [1, 2, 3])')).toThrow(/Example:/);
32+
});
33+
34+
it('should provide user-friendly error when second argument is not an array', function () {
35+
const parser = new Parser();
36+
expect(() => parser.evaluate('f(x) = x * 2; map(f, 123)')).toThrow(
37+
/map\(mapper, array\) expects an array as second argument, got number/
38+
);
39+
});
40+
});
41+
42+
describe('fold()', function () {
43+
it('should provide user-friendly error when first argument is not a function', function () {
44+
const parser = new Parser();
45+
expect(() => parser.evaluate('fold(null, 0, [1, 2, 3])', { null: null })).toThrow(
46+
/fold\(reducer, initial, array\) expects a function as first argument, got null/
47+
);
48+
expect(() => parser.evaluate('fold(null, 0, [1, 2, 3])', { null: null })).toThrow(/Example:/);
49+
});
50+
51+
it('should provide user-friendly error when third argument is not an array', function () {
52+
const parser = new Parser();
53+
expect(() => parser.evaluate('f(a, b) = a + b; fold(f, 0, {a: 1})')).toThrow(
54+
/fold\(reducer, initial, array\) expects an array as third argument, got object/
55+
);
56+
});
57+
});
58+
59+
describe('reduce()', function () {
60+
it('should provide user-friendly error from fold when first argument is not a function', function () {
61+
const parser = new Parser();
62+
expect(() => parser.evaluate('reduce(true, 0, [1, 2, 3])')).toThrow(
63+
/fold\(reducer, initial, array\) expects a function as first argument, got boolean/
64+
);
65+
});
66+
});
67+
68+
describe('find()', function () {
69+
it('should provide user-friendly error when first argument is not a function', function () {
70+
const parser = new Parser();
71+
expect(() => parser.evaluate('find([1, 2], [1, 2, 3])')).toThrow(
72+
/find\(predicate, array\) expects a function as first argument, got array/
73+
);
74+
expect(() => parser.evaluate('find([1, 2], [1, 2, 3])')).toThrow(/Example:/);
75+
});
76+
77+
it('should provide user-friendly error when second argument is not an array', function () {
78+
const parser = new Parser();
79+
expect(() => parser.evaluate('f(x) = x > 0; find(f, 99)')).toThrow(
80+
/find\(predicate, array\) expects an array as second argument, got number/
81+
);
82+
});
83+
});
84+
85+
describe('some()', function () {
86+
it('should provide user-friendly error when first argument is not a function', function () {
87+
const parser = new Parser();
88+
expect(() => parser.evaluate('some(5, [1, 2, 3])')).toThrow(
89+
/some\(predicate, array\) expects a function as first argument, got number/
90+
);
91+
});
92+
93+
it('should provide user-friendly error when second argument is not an array', function () {
94+
const parser = new Parser();
95+
expect(() => parser.evaluate('f(x) = x > 0; some(f, "string")')).toThrow(
96+
/some\(predicate, array\) expects an array as second argument, got string/
97+
);
98+
});
99+
});
100+
101+
describe('every()', function () {
102+
it('should provide user-friendly error when first argument is not a function', function () {
103+
const parser = new Parser();
104+
expect(() => parser.evaluate('every({a: 1}, [1, 2, 3])')).toThrow(
105+
/every\(predicate, array\) expects a function as first argument, got object/
106+
);
107+
});
108+
109+
it('should provide user-friendly error when second argument is not an array', function () {
110+
const parser = new Parser();
111+
expect(() => parser.evaluate('f(x) = x > 0; every(f, false)')).toThrow(
112+
/every\(predicate, array\) expects an array as second argument, got boolean/
113+
);
114+
});
115+
});
116+
117+
describe('indexOf()', function () {
118+
it('should provide user-friendly error when second argument is not an array or string', function () {
119+
const parser = new Parser();
120+
expect(() => parser.evaluate('indexOf(1, 123)')).toThrow(
121+
/indexOf\(target, arrayOrString\) expects a string or array as second argument, got number/
122+
);
123+
expect(() => parser.evaluate('indexOf(1, 123)')).toThrow(/Example:/);
124+
});
125+
});
126+
127+
describe('join()', function () {
128+
it('should provide user-friendly error when second argument is not an array', function () {
129+
const parser = new Parser();
130+
expect(() => parser.evaluate('join(", ", "not array")')).toThrow(
131+
/join\(separator, array\) expects an array as second argument, got string/
132+
);
133+
expect(() => parser.evaluate('join(", ", "not array")')).toThrow(/Example:/);
134+
});
135+
});
136+
137+
describe('sum()', function () {
138+
it('should provide user-friendly error when argument is not an array', function () {
139+
const parser = new Parser();
140+
expect(() => parser.evaluate('sum(42)')).toThrow(
141+
/sum\(array\) expects an array as argument, got number/
142+
);
143+
expect(() => parser.evaluate('sum(42)')).toThrow(/Example:/);
144+
});
145+
});
146+
147+
describe('count()', function () {
148+
it('should provide user-friendly error when argument is not an array', function () {
149+
const parser = new Parser();
150+
expect(() => parser.evaluate('count("string")')).toThrow(
151+
/count\(array\) expects an array as argument, got string/
152+
);
153+
expect(() => parser.evaluate('count("string")')).toThrow(/Example:/);
154+
});
155+
});
156+
157+
describe('unique()', function () {
158+
it('should provide user-friendly error when argument is not an array', function () {
159+
const parser = new Parser();
160+
expect(() => parser.evaluate('unique(123)')).toThrow(
161+
/unique\(array\) expects an array as argument, got number/
162+
);
163+
expect(() => parser.evaluate('unique(123)')).toThrow(/Example:/);
164+
});
165+
});
166+
167+
describe('distinct()', function () {
168+
it('should provide user-friendly error from unique when argument is not an array', function () {
169+
const parser = new Parser();
170+
expect(() => parser.evaluate('distinct({a: 1})')).toThrow(
171+
/unique\(array\) expects an array as argument, got object/
172+
);
173+
});
174+
});
175+
});

0 commit comments

Comments
 (0)