Improve array function error messages with signatures and examples#35
Improve array function error messages with signatures and examples#35Sander-Toonen merged 2 commits intomasterfrom
Conversation
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>
There was a problem hiding this comment.
Pull request overview
This PR makes array function argument errors more user-friendly by including the function signature, the received argument type, and a usage example, and adds tests to validate the new messages.
Changes:
- Added a
getTypeName()helper to produce readable runtime type names (null,array,string, etc.). - Updated thrown error messages in array operations to include signature + expected type + received type + example.
- Added a dedicated test suite validating the new error messages for array functions.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
src/functions/array/operations.ts |
Adds getTypeName() and updates argument-type errors for array operation functions to include signatures and examples. |
test/functions/functions-array-errors.ts |
Adds tests that assert the new error message format (signature/type/example) for array functions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 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/ | ||
| ); | ||
| }); |
There was a problem hiding this comment.
This test validates the main part of the fold(...) error, but it doesn’t assert that the message includes the usage Example: line. Since the PR adds examples to these errors, consider asserting Example: is present here too.
| 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/ | ||
| ); | ||
| }); |
There was a problem hiding this comment.
This test validates the main part of the find(...) error, but it doesn’t assert that the message includes the usage Example: line. Adding an Example: assertion here would better cover the new behavior introduced by this PR.
| 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/ | ||
| ); | ||
| }); |
There was a problem hiding this comment.
Neither of the some(...) error tests asserts that the message contains the Example: line. Since this PR adds examples to these errors, consider asserting Example: is included for both cases to prevent regressions.
| 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/ | ||
| ); | ||
| }); |
There was a problem hiding this comment.
Neither of the every(...) error tests asserts that the message contains the Example: line. Since this PR adds examples to these errors, consider asserting Example: is included for both cases to prevent regressions.
| 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/ | ||
| ); |
There was a problem hiding this comment.
This test currently expects distinct(...) to throw an error message that starts with unique(array) .... Since distinct is user-facing, it would be clearer if the error message used the distinct(array) signature (and included an Example: line) instead of exposing the alias target.
| 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/ | |
| ); | |
| it('should provide user-friendly error when argument is not an array', function () { | |
| const parser = new Parser(); | |
| expect(() => parser.evaluate('distinct({a: 1})')).toThrow( | |
| /distinct\(array\) expects an array as argument, got object/ | |
| ); | |
| expect(() => parser.evaluate('distinct({a: 1})')).toThrow(/Example:/); |
| 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/ |
There was a problem hiding this comment.
This test codifies reduce(...) raising an error message that starts with fold(...). Since reduce is a user-facing function, it would be clearer if the thrown message referenced reduce(reducer, initial, array) (and included a reduce(...) example) rather than leaking the alias implementation detail.
| 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/ | |
| it('should provide user-friendly error when first argument is not a function', function () { | |
| const parser = new Parser(); | |
| expect(() => parser.evaluate('reduce(true, 0, [1, 2, 3])')).toThrow( | |
| /reduce\(reducer, initial, array\) expects a function as first argument, got boolean/ |
| 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:/); |
There was a problem hiding this comment.
The Example: assertion re-evaluates the same expression a second time (which re-parses and re-runs the evaluation). To keep tests faster and avoid any unintended side effects, consider capturing the thrown error once and asserting on its .message for both the main text and the Example: line.
| 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])' | ||
| ); |
There was a problem hiding this comment.
reduce() is an alias for fold(), so any argument-type errors triggered via reduce(...) will now surface an error message that starts with fold(reducer, initial, array) .... That’s confusing for users who called reduce. Consider having reduce() do its own argument validation / error formatting (or catch and rethrow) so the message references reduce(reducer, initial, array) and provides a reduce(...) example.
| throw new Error( | ||
| `unique(array) expects an array as argument, got ${getTypeName(a)}.\n` + | ||
| 'Example: unique([1, 2, 2, 3, 3, 3])' | ||
| ); |
There was a problem hiding this comment.
distinct() is a public alias for unique(), but any argument-type errors triggered via distinct(...) will now mention unique(array) ..., which can be confusing. Consider validating/rethrowing inside distinct() so the message uses the distinct(array) signature (and example) while still delegating the implementation to unique().
| 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/ | ||
| ); | ||
| }); |
There was a problem hiding this comment.
This test validates the main part of the map(...) error, but it doesn’t assert that the message includes the usage Example: line. Since the PR’s goal is to add signature + example to these errors, consider asserting Example: is present here too to prevent regressions.
Array function errors were technical and unhelpful. Updated to include function signature, received type, and usage example.
Changes
getTypeName()helper for user-friendly type descriptionsfilter,map,fold,find,some,every,indexOf,join,sum,count,uniqueBefore/After
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.