diff --git a/packages/reactivity/__tests__/reactive.spec.ts b/packages/reactivity/__tests__/reactive.spec.ts index a3ba6a39c..98187d814 100644 --- a/packages/reactivity/__tests__/reactive.spec.ts +++ b/packages/reactivity/__tests__/reactive.spec.ts @@ -207,6 +207,13 @@ describe('reactivity/reactive', () => { expect(raw).toBe(original) }) + test('toRaw on collection types using reactive as prototype', () => { + const originalCollection = reactive(new Map()) + const collection = Object.create(originalCollection) + const rawCollection = toRaw(collection) + expect(rawCollection).toBe(collection) + }) + test('should not unwrap Ref', () => { const observedNumberRef = reactive(ref(1)) const observedObjectRef = reactive(ref({ foo: 1 })) diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts index faec3012f..c0e608fec 100644 --- a/packages/reactivity/src/baseHandlers.ts +++ b/packages/reactivity/src/baseHandlers.ts @@ -46,6 +46,30 @@ function hasOwnProperty(this: object, key: unknown) { return obj.hasOwnProperty(key as string) } +export function getRawValue( + receiver: object, + isReadonly: boolean, + isShallow: boolean, + target: Target, +): Target | undefined { + if ( + receiver === + (isReadonly + ? isShallow + ? shallowReadonlyMap + : readonlyMap + : isShallow + ? shallowReactiveMap + : reactiveMap + ).get(target) || + // receiver is not the reactive proxy, but has the same prototype + // this means the receiver is a user proxy of the reactive proxy + Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver) + ) { + return target + } +} + class BaseReactiveHandler implements ProxyHandler { constructor( protected readonly _isReadonly = false, @@ -64,24 +88,7 @@ class BaseReactiveHandler implements ProxyHandler { } else if (key === ReactiveFlags.IS_SHALLOW) { return isShallow } else if (key === ReactiveFlags.RAW) { - if ( - receiver === - (isReadonly - ? isShallow - ? shallowReadonlyMap - : readonlyMap - : isShallow - ? shallowReactiveMap - : reactiveMap - ).get(target) || - // receiver is not the reactive proxy, but has the same prototype - // this means the receiver is a user proxy of the reactive proxy - Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver) - ) { - return target - } - // early return undefined - return + return getRawValue(receiver, isReadonly, isShallow, target) } const targetIsArray = isArray(target) diff --git a/packages/reactivity/src/collectionHandlers.ts b/packages/reactivity/src/collectionHandlers.ts index 048b7f388..f2d2a4db5 100644 --- a/packages/reactivity/src/collectionHandlers.ts +++ b/packages/reactivity/src/collectionHandlers.ts @@ -17,6 +17,7 @@ import { toRawType, } from '@vue/shared' import { warn } from './warning' +import { getRawValue } from './baseHandlers' type CollectionTypes = IterableCollections | WeakCollections @@ -273,7 +274,7 @@ function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) { } else if (key === ReactiveFlags.IS_READONLY) { return isReadonly } else if (key === ReactiveFlags.RAW) { - return target + return getRawValue(receiver, isReadonly, shallow, target) } return Reflect.get(