diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index aaf53242..b03e916d 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -1,15 +1,17 @@ export { ref, - unref, shallowRef, isRef, toRef, toRefs, + unref, + proxyRefs, customRef, triggerRef, Ref, - UnwrapRef, ToRefs, + UnwrapRef, + ShallowUnwrapRef, RefUnwrapBailTypes } from './ref' export { diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index 25473b4c..174494e8 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -1,7 +1,7 @@ import { track, trigger } from './effect' import { TrackOpTypes, TriggerOpTypes } from './operations' import { isObject, hasChanged } from '@vue/shared' -import { reactive, isProxy, toRaw } from './reactive' +import { reactive, isProxy, toRaw, isReactive } from './reactive' import { CollectionTypes } from './collectionHandlers' declare const RefSymbol: unique symbol @@ -71,6 +71,27 @@ export function unref(ref: T): T extends Ref ? V : T { return isRef(ref) ? (ref.value as any) : ref } +const shallowUnwrapHandlers: ProxyHandler = { + get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)), + set: (target, key, value, receiver) => { + const oldValue = target[key] + if (isRef(oldValue) && !isRef(value)) { + oldValue.value = value + return true + } else { + return Reflect.set(target, key, value, receiver) + } + } +} + +export function proxyRefs( + objectWithRefs: T +): ShallowUnwrapRef { + return isReactive(objectWithRefs) + ? objectWithRefs + : new Proxy(objectWithRefs, shallowUnwrapHandlers) +} + export type CustomRefFactory = ( track: () => void, trigger: () => void @@ -146,6 +167,10 @@ type BaseTypes = string | number | boolean */ export interface RefUnwrapBailTypes {} +export type ShallowUnwrapRef = { + [K in keyof T]: T[K] extends Ref ? V : T[K] +} + export type UnwrapRef = T extends Ref ? UnwrapRefSimple : UnwrapRefSimple diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index c0ea6ab3..12eaa76b 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -1,10 +1,10 @@ import { VNode, VNodeChild, isVNode } from './vnode' import { - reactive, ReactiveEffect, pauseTracking, resetTracking, - shallowReadonly + shallowReadonly, + proxyRefs } from '@vue/reactivity' import { CreateComponentPublicInstance, @@ -548,7 +548,7 @@ export function handleSetupResult( } // setup returned bindings. // assuming a render function compiled from template is present. - instance.setupState = reactive(setupResult) + instance.setupState = proxyRefs(setupResult) if (__DEV__) { exposeSetupStateOnRenderContext(instance) } diff --git a/packages/runtime-core/src/componentProxy.ts b/packages/runtime-core/src/componentProxy.ts index d2b78318..e043d93e 100644 --- a/packages/runtime-core/src/componentProxy.ts +++ b/packages/runtime-core/src/componentProxy.ts @@ -10,12 +10,12 @@ import { } from '@vue/shared' import { ReactiveEffect, - UnwrapRef, toRaw, shallowReadonly, ReactiveFlags, track, - TrackOpTypes + TrackOpTypes, + ShallowUnwrapRef } from '@vue/reactivity' import { ExtractComputedReturns, @@ -154,7 +154,7 @@ export type ComponentPublicInstance< $nextTick: typeof nextTick $watch: typeof instanceWatch } & P & - UnwrapRef & + ShallowUnwrapRef & D & ExtractComputedReturns & M & diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 757a2138..14283b5d 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -8,6 +8,7 @@ export { readonly, // utilities unref, + proxyRefs, isRef, toRef, toRefs, @@ -125,6 +126,7 @@ export { ComputedRef, WritableComputedRef, UnwrapRef, + ShallowUnwrapRef, WritableComputedOptions, ToRefs, DeepReadonly diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx index dcbad6a0..f7dacfcb 100644 --- a/test-dts/defineComponent.test-d.tsx +++ b/test-dts/defineComponent.test-d.tsx @@ -146,7 +146,7 @@ describe('with object props', () => { // assert setup context unwrapping expectType(this.c) - expectType(this.d.e) + expectType(this.d.e.value) expectType(this.f.g) // setup context properties should be mutable