Skip to content

Commit 4085def

Browse files
authored
fix(reactivity): ensure extended method arguments are not lost (#11574)
close #11570
1 parent 63b7c01 commit 4085def

File tree

2 files changed

+113
-11
lines changed

2 files changed

+113
-11
lines changed

packages/reactivity/__tests__/reactiveArray.spec.ts

+95
Original file line numberDiff line numberDiff line change
@@ -622,5 +622,100 @@ describe('reactivity/reactive/Array', () => {
622622
const firstItem = Array.from(deep.values())[0]
623623
expect(isReactive(firstItem)).toBe(true)
624624
})
625+
626+
test('extend methods', () => {
627+
class Collection extends Array {
628+
// @ts-expect-error
629+
every(foo: any, bar: any, baz: any) {
630+
expect(foo).toBe('foo')
631+
expect(bar).toBe('bar')
632+
expect(baz).toBe('baz')
633+
return super.every(obj => obj.id === foo)
634+
}
635+
636+
// @ts-expect-error
637+
filter(foo: any, bar: any, baz: any) {
638+
expect(foo).toBe('foo')
639+
expect(bar).toBe('bar')
640+
expect(baz).toBe('baz')
641+
return super.filter(obj => obj.id === foo)
642+
}
643+
644+
// @ts-expect-error
645+
find(foo: any, bar: any, baz: any) {
646+
expect(foo).toBe('foo')
647+
expect(bar).toBe('bar')
648+
expect(baz).toBe('baz')
649+
return super.find(obj => obj.id === foo)
650+
}
651+
652+
// @ts-expect-error
653+
findIndex(foo: any, bar: any, baz: any) {
654+
expect(foo).toBe('foo')
655+
expect(bar).toBe('bar')
656+
expect(baz).toBe('baz')
657+
return super.findIndex(obj => obj.id === bar)
658+
}
659+
660+
findLast(foo: any, bar: any, baz: any) {
661+
expect(foo).toBe('foo')
662+
expect(bar).toBe('bar')
663+
expect(baz).toBe('baz')
664+
// @ts-expect-error our code is limited to es2016 but user code is not
665+
return super.findLast(obj => obj.id === bar)
666+
}
667+
668+
findLastIndex(foo: any, bar: any, baz: any) {
669+
expect(foo).toBe('foo')
670+
expect(bar).toBe('bar')
671+
expect(baz).toBe('baz')
672+
return super.findIndex(obj => obj.id === bar)
673+
}
674+
675+
// @ts-expect-error
676+
forEach(foo: any, bar: any, baz: any) {
677+
expect(foo).toBe('foo')
678+
expect(bar).toBe('bar')
679+
expect(baz).toBe('baz')
680+
}
681+
682+
// @ts-expect-error
683+
map(foo: any, bar: any, baz: any) {
684+
expect(foo).toBe('foo')
685+
expect(bar).toBe('bar')
686+
expect(baz).toBe('baz')
687+
return super.map(obj => obj.value)
688+
}
689+
690+
// @ts-expect-error
691+
some(foo: any, bar: any, baz: any) {
692+
expect(foo).toBe('foo')
693+
expect(bar).toBe('bar')
694+
expect(baz).toBe('baz')
695+
return super.some(obj => obj.id === baz)
696+
}
697+
}
698+
699+
const state = reactive({
700+
things: new Collection(),
701+
})
702+
703+
const foo = { id: 'foo', value: '1' }
704+
const bar = { id: 'bar', value: '2' }
705+
const baz = { id: 'baz', value: '3' }
706+
state.things.push(foo)
707+
state.things.push(bar)
708+
state.things.push(baz)
709+
710+
expect(state.things.every('foo', 'bar', 'baz')).toBe(false)
711+
expect(state.things.filter('foo', 'bar', 'baz')).toEqual([foo])
712+
expect(state.things.find('foo', 'bar', 'baz')).toBe(foo)
713+
expect(state.things.findIndex('foo', 'bar', 'baz')).toBe(1)
714+
expect(state.things.findLast('foo', 'bar', 'baz')).toBe(bar)
715+
expect(state.things.findLastIndex('foo', 'bar', 'baz')).toBe(1)
716+
expect(state.things.forEach('foo', 'bar', 'baz')).toBeUndefined()
717+
expect(state.things.map('foo', 'bar', 'baz')).toEqual(['1', '2', '3'])
718+
expect(state.things.some('foo', 'bar', 'baz')).toBe(true)
719+
})
625720
})
626721
})

packages/reactivity/src/arrayInstrumentations.ts

+18-11
Original file line numberDiff line numberDiff line change
@@ -47,42 +47,42 @@ export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
4747
fn: (item: unknown, index: number, array: unknown[]) => unknown,
4848
thisArg?: unknown,
4949
) {
50-
return apply(this, 'every', fn, thisArg)
50+
return apply(this, 'every', fn, thisArg, undefined, arguments)
5151
},
5252

5353
filter(
5454
fn: (item: unknown, index: number, array: unknown[]) => unknown,
5555
thisArg?: unknown,
5656
) {
57-
return apply(this, 'filter', fn, thisArg, v => v.map(toReactive))
57+
return apply(this, 'filter', fn, thisArg, v => v.map(toReactive), arguments)
5858
},
5959

6060
find(
6161
fn: (item: unknown, index: number, array: unknown[]) => boolean,
6262
thisArg?: unknown,
6363
) {
64-
return apply(this, 'find', fn, thisArg, toReactive)
64+
return apply(this, 'find', fn, thisArg, toReactive, arguments)
6565
},
6666

6767
findIndex(
6868
fn: (item: unknown, index: number, array: unknown[]) => boolean,
6969
thisArg?: unknown,
7070
) {
71-
return apply(this, 'findIndex', fn, thisArg)
71+
return apply(this, 'findIndex', fn, thisArg, undefined, arguments)
7272
},
7373

7474
findLast(
7575
fn: (item: unknown, index: number, array: unknown[]) => boolean,
7676
thisArg?: unknown,
7777
) {
78-
return apply(this, 'findLast', fn, thisArg, toReactive)
78+
return apply(this, 'findLast', fn, thisArg, toReactive, arguments)
7979
},
8080

8181
findLastIndex(
8282
fn: (item: unknown, index: number, array: unknown[]) => boolean,
8383
thisArg?: unknown,
8484
) {
85-
return apply(this, 'findLastIndex', fn, thisArg)
85+
return apply(this, 'findLastIndex', fn, thisArg, undefined, arguments)
8686
},
8787

8888
// flat, flatMap could benefit from ARRAY_ITERATE but are not straight-forward to implement
@@ -91,7 +91,7 @@ export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
9191
fn: (item: unknown, index: number, array: unknown[]) => unknown,
9292
thisArg?: unknown,
9393
) {
94-
return apply(this, 'forEach', fn, thisArg)
94+
return apply(this, 'forEach', fn, thisArg, undefined, arguments)
9595
},
9696

9797
includes(...args: unknown[]) {
@@ -116,7 +116,7 @@ export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
116116
fn: (item: unknown, index: number, array: unknown[]) => unknown,
117117
thisArg?: unknown,
118118
) {
119-
return apply(this, 'map', fn, thisArg)
119+
return apply(this, 'map', fn, thisArg, undefined, arguments)
120120
},
121121

122122
pop() {
@@ -161,7 +161,7 @@ export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
161161
fn: (item: unknown, index: number, array: unknown[]) => unknown,
162162
thisArg?: unknown,
163163
) {
164-
return apply(this, 'some', fn, thisArg)
164+
return apply(this, 'some', fn, thisArg, undefined, arguments)
165165
},
166166

167167
splice(...args: unknown[]) {
@@ -227,6 +227,7 @@ function iterator(
227227
// higher than that
228228
type ArrayMethods = keyof Array<any> | 'findLast' | 'findLastIndex'
229229

230+
const arrayProto = Array.prototype
230231
// instrument functions that read (potentially) all items
231232
// to take ARRAY_ITERATE dependency
232233
function apply(
@@ -235,8 +236,15 @@ function apply(
235236
fn: (item: unknown, index: number, array: unknown[]) => unknown,
236237
thisArg?: unknown,
237238
wrappedRetFn?: (result: any) => unknown,
239+
args?: IArguments,
238240
) {
239241
const arr = shallowReadArray(self)
242+
let methodFn
243+
// @ts-expect-error our code is limited to es2016 but user code is not
244+
if ((methodFn = arr[method]) !== arrayProto[method]) {
245+
return methodFn.apply(arr, args)
246+
}
247+
240248
let needsWrap = false
241249
let wrappedFn = fn
242250
if (arr !== self) {
@@ -251,8 +259,7 @@ function apply(
251259
}
252260
}
253261
}
254-
// @ts-expect-error our code is limited to es2016 but user code is not
255-
const result = arr[method](wrappedFn, thisArg)
262+
const result = methodFn.call(arr, wrappedFn, thisArg)
256263
return needsWrap && wrappedRetFn ? wrappedRetFn(result) : result
257264
}
258265

0 commit comments

Comments
 (0)