From 75ecbd51b1f52e5d2f554850fe7824d28170c4ea Mon Sep 17 00:00:00 2001 From: Sojin Park Date: Fri, 29 May 2026 20:52:21 +0900 Subject: [PATCH 1/2] fix[reduce, reduceRight]: respect nullish accumulators Match lodash by treating accumulator presence as an argument-count check, so explicit null and undefined initial values are passed to iteratees. Add compat coverage for reduce and reduceRight over arrays and objects. --- src/compat/array/reduce.spec.ts | 33 ++++++++++++++++++++++++++++ src/compat/array/reduce.ts | 6 +++-- src/compat/array/reduceRight.spec.ts | 33 ++++++++++++++++++++++++++++ src/compat/array/reduceRight.ts | 6 +++-- 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/compat/array/reduce.spec.ts b/src/compat/array/reduce.spec.ts index aa8f3ed347..d56fa1d062 100644 --- a/src/compat/array/reduce.spec.ts +++ b/src/compat/array/reduce.spec.ts @@ -54,6 +54,39 @@ describe('reduce', () => { expect(actual).toBe(undefined); }); + it('should respect nullish initial `accumulator` values', () => { + const array = [1, 2, 3]; + const object = { a: 1, b: 2 }; + + for (const accumulator of [null, undefined]) { + let args: unknown[] | undefined; + + reduce( + array, + function () { + // eslint-disable-next-line + args || (args = Array.prototype.slice.call(arguments)); + }, + accumulator + ); + + expect(args).toEqual([accumulator, 1, 0, array]); + + args = undefined; + + reduce( + object, + function () { + // eslint-disable-next-line + args || (args = Array.prototype.slice.call(arguments)); + }, + accumulator + ); + + expect(args).toEqual([accumulator, 1, 'a', object]); + } + }); + it(`should return \`undefined\` for empty collections when no \`accumulator\` is given (test in IE > 9 and modern browsers)`, () => { const array: any[] = []; const object = { 0: 1, length: 0 }; diff --git a/src/compat/array/reduce.ts b/src/compat/array/reduce.ts index ff8ec74a24..b66257b74e 100644 --- a/src/compat/array/reduce.ts +++ b/src/compat/array/reduce.ts @@ -131,6 +131,8 @@ export function reduce( iteratee: (accumulator: any, value: any, index: any, collection: any) => any = identity, accumulator?: any ): any { + const hasAccumulator = arguments.length >= 3; + if (!collection) { return accumulator; } @@ -141,14 +143,14 @@ export function reduce( if (isArrayLike(collection)) { keys = range(0, collection.length); - if (accumulator == null && collection.length > 0) { + if (!hasAccumulator && collection.length > 0) { accumulator = (collection as ArrayLike)[0]; startIndex += 1; } } else { keys = Object.keys(collection); - if (accumulator == null) { + if (!hasAccumulator && keys.length > 0) { accumulator = (collection as any)[keys[0]]; startIndex += 1; } diff --git a/src/compat/array/reduceRight.spec.ts b/src/compat/array/reduceRight.spec.ts index c88b962a7e..53368ed7d1 100644 --- a/src/compat/array/reduceRight.spec.ts +++ b/src/compat/array/reduceRight.spec.ts @@ -116,6 +116,39 @@ describe('reduceRight', () => { expect(actual).toBe(undefined); }); + it('should respect nullish initial `accumulator` values', () => { + const array = [1, 2, 3]; + const object = { a: 1, b: 2 }; + + for (const accumulator of [null, undefined]) { + let args: unknown[] | undefined; + + reduceRight( + array, + function () { + // eslint-disable-next-line + args || (args = Array.prototype.slice.call(arguments)); + }, + accumulator + ); + + expect(args).toEqual([accumulator, 3, 2, array]); + + args = undefined; + + reduceRight( + object, + function () { + // eslint-disable-next-line + args || (args = Array.prototype.slice.call(arguments)); + }, + accumulator + ); + + expect(args).toEqual([accumulator, 2, 'b', object]); + } + }); + it(`should return \`undefined\` for empty collections when no \`accumulator\` is given (test in IE > 9 and modern browsers)`, () => { const array: any[] = []; const object = { 0: 1, length: 0 }; diff --git a/src/compat/array/reduceRight.ts b/src/compat/array/reduceRight.ts index e46ba3c38a..1117f3fe5c 100644 --- a/src/compat/array/reduceRight.ts +++ b/src/compat/array/reduceRight.ts @@ -172,6 +172,8 @@ export function reduceRight( iteratee: (accumulator: any, value: any, index: any, collection: any) => any = identity, accumulator?: any ): any { + const hasAccumulator = arguments.length >= 3; + if (!collection) { return accumulator; } @@ -182,7 +184,7 @@ export function reduceRight( if (isArrayLike(collection)) { keys = range(0, collection.length).reverse(); - if (accumulator == null && collection.length > 0) { + if (!hasAccumulator && collection.length > 0) { accumulator = (collection as ArrayLike)[collection.length - 1]; startIndex = 1; } else { @@ -191,7 +193,7 @@ export function reduceRight( } else { keys = Object.keys(collection).reverse(); - if (accumulator == null) { + if (!hasAccumulator && keys.length > 0) { accumulator = (collection as any)[keys[0]]; startIndex = 1; } else { From 331609464aa7006714ca215466adce1cde703aa7 Mon Sep 17 00:00:00 2001 From: Sojin Park Date: Fri, 29 May 2026 20:52:24 +0900 Subject: [PATCH 2/2] chore(docs): satisfy curly lint rule Add braces to existing single-line early returns so the repository lint command can complete without errors. --- docs/.vitepress/components/FlavorDropdown.vue | 8 ++++++-- docs/.vitepress/components/Playground.vue | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/.vitepress/components/FlavorDropdown.vue b/docs/.vitepress/components/FlavorDropdown.vue index 9ba68192c9..cf381f4dc9 100644 --- a/docs/.vitepress/components/FlavorDropdown.vue +++ b/docs/.vitepress/components/FlavorDropdown.vue @@ -19,12 +19,16 @@ function toggle() { function select(option: FlavorSpec) { open.value = false; - if (option.value === currentFlavor.value.value) return; + if (option.value === currentFlavor.value.value) { + return; + } router.go(flavorIntroPath({ flavor: option, localePrefix: localePrefix.value })); } function onDocumentPointerDown(event: PointerEvent) { - if (!open.value || !root.value) return; + if (!open.value || !root.value) { + return; + } if (!root.value.contains(event.target as Node)) { open.value = false; } diff --git a/docs/.vitepress/components/Playground.vue b/docs/.vitepress/components/Playground.vue index c835816b6a..879f63dfae 100644 --- a/docs/.vitepress/components/Playground.vue +++ b/docs/.vitepress/components/Playground.vue @@ -227,7 +227,9 @@ const openCategories = ref(new Set(['array'])); const sandpackKey = ref(0); const docUrl = computed(() => { - if (!selectedFunction.value || !selectedCategory.value) return null; + if (!selectedFunction.value || !selectedCategory.value) { + return null; + } const locale = lang.value; const prefix = locale && locale !== 'en' && locale !== 'root' ? `/${locale}` : ''; return `${prefix}/reference/${selectedCategory.value}/${selectedFunction.value}`;