]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(reactivity): ensure extended method arguments are not lost (#11574)
authoredison <daiwei521@126.com>
Sat, 10 Aug 2024 07:54:38 +0000 (15:54 +0800)
committerGitHub <noreply@github.com>
Sat, 10 Aug 2024 07:54:38 +0000 (15:54 +0800)
close #11570

packages/reactivity/__tests__/reactiveArray.spec.ts
packages/reactivity/src/arrayInstrumentations.ts

index 6ea3c953957433b6f5534bbe4868b9cddb7b6b2a..960c16e0b3bf1a3fb19f7792f746852baecf6906 100644 (file)
@@ -622,5 +622,100 @@ describe('reactivity/reactive/Array', () => {
       const firstItem = Array.from(deep.values())[0]
       expect(isReactive(firstItem)).toBe(true)
     })
+
+    test('extend methods', () => {
+      class Collection extends Array {
+        // @ts-expect-error
+        every(foo: any, bar: any, baz: any) {
+          expect(foo).toBe('foo')
+          expect(bar).toBe('bar')
+          expect(baz).toBe('baz')
+          return super.every(obj => obj.id === foo)
+        }
+
+        // @ts-expect-error
+        filter(foo: any, bar: any, baz: any) {
+          expect(foo).toBe('foo')
+          expect(bar).toBe('bar')
+          expect(baz).toBe('baz')
+          return super.filter(obj => obj.id === foo)
+        }
+
+        // @ts-expect-error
+        find(foo: any, bar: any, baz: any) {
+          expect(foo).toBe('foo')
+          expect(bar).toBe('bar')
+          expect(baz).toBe('baz')
+          return super.find(obj => obj.id === foo)
+        }
+
+        // @ts-expect-error
+        findIndex(foo: any, bar: any, baz: any) {
+          expect(foo).toBe('foo')
+          expect(bar).toBe('bar')
+          expect(baz).toBe('baz')
+          return super.findIndex(obj => obj.id === bar)
+        }
+
+        findLast(foo: any, bar: any, baz: any) {
+          expect(foo).toBe('foo')
+          expect(bar).toBe('bar')
+          expect(baz).toBe('baz')
+          // @ts-expect-error our code is limited to es2016 but user code is not
+          return super.findLast(obj => obj.id === bar)
+        }
+
+        findLastIndex(foo: any, bar: any, baz: any) {
+          expect(foo).toBe('foo')
+          expect(bar).toBe('bar')
+          expect(baz).toBe('baz')
+          return super.findIndex(obj => obj.id === bar)
+        }
+
+        // @ts-expect-error
+        forEach(foo: any, bar: any, baz: any) {
+          expect(foo).toBe('foo')
+          expect(bar).toBe('bar')
+          expect(baz).toBe('baz')
+        }
+
+        // @ts-expect-error
+        map(foo: any, bar: any, baz: any) {
+          expect(foo).toBe('foo')
+          expect(bar).toBe('bar')
+          expect(baz).toBe('baz')
+          return super.map(obj => obj.value)
+        }
+
+        // @ts-expect-error
+        some(foo: any, bar: any, baz: any) {
+          expect(foo).toBe('foo')
+          expect(bar).toBe('bar')
+          expect(baz).toBe('baz')
+          return super.some(obj => obj.id === baz)
+        }
+      }
+
+      const state = reactive({
+        things: new Collection(),
+      })
+
+      const foo = { id: 'foo', value: '1' }
+      const bar = { id: 'bar', value: '2' }
+      const baz = { id: 'baz', value: '3' }
+      state.things.push(foo)
+      state.things.push(bar)
+      state.things.push(baz)
+
+      expect(state.things.every('foo', 'bar', 'baz')).toBe(false)
+      expect(state.things.filter('foo', 'bar', 'baz')).toEqual([foo])
+      expect(state.things.find('foo', 'bar', 'baz')).toBe(foo)
+      expect(state.things.findIndex('foo', 'bar', 'baz')).toBe(1)
+      expect(state.things.findLast('foo', 'bar', 'baz')).toBe(bar)
+      expect(state.things.findLastIndex('foo', 'bar', 'baz')).toBe(1)
+      expect(state.things.forEach('foo', 'bar', 'baz')).toBeUndefined()
+      expect(state.things.map('foo', 'bar', 'baz')).toEqual(['1', '2', '3'])
+      expect(state.things.some('foo', 'bar', 'baz')).toBe(true)
+    })
   })
 })
index 69f08d6c5d1d52e7157422e7cd30985c54dece0b..58c31835dfd9c88f3cfcd0220ee9090a4cfd6643 100644 (file)
@@ -47,42 +47,42 @@ export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
     fn: (item: unknown, index: number, array: unknown[]) => unknown,
     thisArg?: unknown,
   ) {
-    return apply(this, 'every', fn, thisArg)
+    return apply(this, 'every', fn, thisArg, undefined, arguments)
   },
 
   filter(
     fn: (item: unknown, index: number, array: unknown[]) => unknown,
     thisArg?: unknown,
   ) {
-    return apply(this, 'filter', fn, thisArg, v => v.map(toReactive))
+    return apply(this, 'filter', fn, thisArg, v => v.map(toReactive), arguments)
   },
 
   find(
     fn: (item: unknown, index: number, array: unknown[]) => boolean,
     thisArg?: unknown,
   ) {
-    return apply(this, 'find', fn, thisArg, toReactive)
+    return apply(this, 'find', fn, thisArg, toReactive, arguments)
   },
 
   findIndex(
     fn: (item: unknown, index: number, array: unknown[]) => boolean,
     thisArg?: unknown,
   ) {
-    return apply(this, 'findIndex', fn, thisArg)
+    return apply(this, 'findIndex', fn, thisArg, undefined, arguments)
   },
 
   findLast(
     fn: (item: unknown, index: number, array: unknown[]) => boolean,
     thisArg?: unknown,
   ) {
-    return apply(this, 'findLast', fn, thisArg, toReactive)
+    return apply(this, 'findLast', fn, thisArg, toReactive, arguments)
   },
 
   findLastIndex(
     fn: (item: unknown, index: number, array: unknown[]) => boolean,
     thisArg?: unknown,
   ) {
-    return apply(this, 'findLastIndex', fn, thisArg)
+    return apply(this, 'findLastIndex', fn, thisArg, undefined, arguments)
   },
 
   // 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>{
     fn: (item: unknown, index: number, array: unknown[]) => unknown,
     thisArg?: unknown,
   ) {
-    return apply(this, 'forEach', fn, thisArg)
+    return apply(this, 'forEach', fn, thisArg, undefined, arguments)
   },
 
   includes(...args: unknown[]) {
@@ -116,7 +116,7 @@ export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
     fn: (item: unknown, index: number, array: unknown[]) => unknown,
     thisArg?: unknown,
   ) {
-    return apply(this, 'map', fn, thisArg)
+    return apply(this, 'map', fn, thisArg, undefined, arguments)
   },
 
   pop() {
@@ -161,7 +161,7 @@ export const arrayInstrumentations: Record<string | symbol, Function> = <any>{
     fn: (item: unknown, index: number, array: unknown[]) => unknown,
     thisArg?: unknown,
   ) {
-    return apply(this, 'some', fn, thisArg)
+    return apply(this, 'some', fn, thisArg, undefined, arguments)
   },
 
   splice(...args: unknown[]) {
@@ -227,6 +227,7 @@ function iterator(
 // higher than that
 type ArrayMethods = keyof Array<any> | 'findLast' | 'findLastIndex'
 
+const arrayProto = Array.prototype
 // instrument functions that read (potentially) all items
 // to take ARRAY_ITERATE dependency
 function apply(
@@ -235,8 +236,15 @@ function apply(
   fn: (item: unknown, index: number, array: unknown[]) => unknown,
   thisArg?: unknown,
   wrappedRetFn?: (result: any) => unknown,
+  args?: IArguments,
 ) {
   const arr = shallowReadArray(self)
+  let methodFn
+  // @ts-expect-error our code is limited to es2016 but user code is not
+  if ((methodFn = arr[method]) !== arrayProto[method]) {
+    return methodFn.apply(arr, args)
+  }
+
   let needsWrap = false
   let wrappedFn = fn
   if (arr !== self) {
@@ -251,8 +259,7 @@ function apply(
       }
     }
   }
-  // @ts-expect-error our code is limited to es2016 but user code is not
-  const result = arr[method](wrappedFn, thisArg)
+  const result = methodFn.call(arr, wrappedFn, thisArg)
   return needsWrap && wrappedRetFn ? wrappedRetFn(result) : result
 }