From 02fea35f9d777ce8011452d28509ec5f09845c0c Mon Sep 17 00:00:00 2001 From: bill Date: Mon, 17 Aug 2026 17:24:59 +1000 Subject: [PATCH] fix: isNameInFieldArray fails to recognise bracket-notation array paths causing nested errors overwriting --- src/__tests__/toNestErrors.ts | 65 +++++++++++++++++++++++++++++++++++ src/toNestErrors.ts | 2 +- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/__tests__/toNestErrors.ts b/src/__tests__/toNestErrors.ts index 80d9d7e5..8b6768a9 100644 --- a/src/__tests__/toNestErrors.ts +++ b/src/__tests__/toNestErrors.ts @@ -359,6 +359,71 @@ test('does not throw SyntaxError for field names containing regex metacharacters ).not.toThrow(); }); +test('preserves nested field array errors when the error path uses bracket notation (#875)', () => { + const result = toNestErrors( + { + 'scenarios[0].rows': { type: 'root-error', message: 'rows required' }, + 'scenarios[0].rows[0].values': { + type: 'required', + message: 'value required', + }, + }, + { + // Mirrors react-hook-form's real `_fields`, which is a genuine nested + // tree (unlike the flat dot-keyed mocks used elsewhere in this file), + // so that `get()` resolves bracket-notation paths the same way it + // would in production. + fields: { + scenarios: { + 0: { + rows: Object.assign( + { + name: 'scenarios.0.rows', + ref: { name: 'scenarios.0.rows' }, + }, + { + 0: { + values: { + name: 'scenarios.0.rows.0.values', + ref: { name: 'scenarios.0.rows.0.values' }, + }, + }, + }, + ), + }, + }, + } as any as Record, + names: [ + 'scenarios.0.rows', + 'scenarios.0.rows.0.values', + 'scenarios.0.rows.1.values', + ], + shouldUseNativeValidation: false, + }, + ); + + expect(result).toEqual({ + scenarios: [ + { + rows: { + '0': { + values: { + type: 'required', + message: 'value required', + ref: { name: 'scenarios.0.rows.0.values' }, + }, + }, + root: { + type: 'root-error', + message: 'rows required', + ref: { name: 'scenarios.0.rows' }, + }, + }, + }, + ], + }); +}); + test('should correctly validate object with special characters', () => { const result = toNestErrors( { '[array-2]': { type: 'string', message: 'string is required' } }, diff --git a/src/toNestErrors.ts b/src/toNestErrors.ts index 4326bce4..696af421 100644 --- a/src/toNestErrors.ts +++ b/src/toNestErrors.ts @@ -48,7 +48,7 @@ const isNameInFieldArray = ( // Removes brackets to match react-hook-form's `set` method behavior. function escapeBrackets(input: string): string { - return input.replace(/[\[\]]/g, ''); + return input.replace(/\[(\d+)]/g, '.$1').replace(/[[\]]/g, ''); } // Removes brackets then escapes regex metacharacters so a field name can be