diff --git a/CHANGELOG.md b/CHANGELOG.md index b4081da862..546bf8b1e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added - Added a message after `npm i hyperformula` that points AI assistants to the SKILLS file. The message needs `--foreground-scripts` to be visible in npm, and pnpm 10 needs the `postinstall` script to be approved first. [#1829](https://github.com/handsontable/hyperformula/pull/1829) +- Added `DetailedCellError.hasMessage`, so a consumer can tell a cell error that never carried a message (e.g. a custom function that omitted one) apart from one with a deliberately empty message — both previously surfaced identically as `message: ''`. [#1547](https://github.com/handsontable/hyperformula/issues/1547) ### Changed diff --git a/docs/guide/custom-functions.md b/docs/guide/custom-functions.md index 5a6021ef2d..ce5cfc6151 100644 --- a/docs/guide/custom-functions.md +++ b/docs/guide/custom-functions.md @@ -326,6 +326,12 @@ custom error messages. Put them to good use: let your users know what caused the error and how to avoid it in the future. ::: +Every error the engine raises while evaluating a formula carries a message. A custom function's +`message` argument stays optional — if you omit it, a consumer reading the resulting +`DetailedCellError` sees `message` as an empty string, the same value HyperFormula uses +for "no message". To tell the two cases apart, check `hasMessage`: `true` means a +message was set (even if it's a deliberately empty string), `false` means none was. + ### Test your function To make sure your function works correctly, add unit tests. Use a JavaScript diff --git a/hyperformula/src/CellValue.ts b/hyperformula/src/CellValue.ts index 47930aad99..ffae216791 100644 --- a/hyperformula/src/CellValue.ts +++ b/hyperformula/src/CellValue.ts @@ -12,6 +12,18 @@ export class DetailedCellError { public readonly type: ErrorType public readonly message: string + /** + * Whether the underlying error carried a message at all. + * + * `message` collapses "no message" and "an empty message" into `''`; this flag + * keeps them apart for consumers that need to know whether a cause was stated. + * Errors the engine raises while evaluating a formula always carry a message. A + * `false` here means nobody stated a cause: a custom function that did not supply + * one, or an error value a user typed straight into a cell, where the cause is the + * typing itself and `originFunction` says so. + */ + public readonly hasMessage: boolean + constructor( error: CellError, public readonly value: string, @@ -19,6 +31,7 @@ export class DetailedCellError { ) { this.type = error.type this.message = error.message ?? '' + this.hasMessage = error.message !== undefined } public toString(): string { diff --git a/hyperformula/test/_setupFiles/matchers/cellErrorComparison.ts b/hyperformula/test/_setupFiles/matchers/cellErrorComparison.ts index 2a9dfa15cf..3729bffec1 100644 --- a/hyperformula/test/_setupFiles/matchers/cellErrorComparison.ts +++ b/hyperformula/test/_setupFiles/matchers/cellErrorComparison.ts @@ -36,6 +36,7 @@ const IGNORED_IN_STRUCTURAL_COMPARE = { address: undefined, originFunction: undefined, argumentIndex: undefined, + hasMessage: undefined, propagated: undefined, originAddress: undefined, originAddressVersion: undefined,