Repository: vuejs/composition-api
Pull request #765 (2021-07-16 02:40:44)

fix(readonly): align behavior with vue-next.


Patch:
diff --git a/src/apis/createElement.ts b/src/apis/createElement.ts
index 6e05d9d..6302405 100644
--- a/src/apis/createElement.ts
+++ b/src/apis/createElement.ts
@@ -22,4 +22,4 @@ export const createElement = function createElement(...args: any) {
   }
 
   return instance.$createElement.apply(instance, args)
-} as any as CreateElement
+} as CreateElement
diff --git a/src/apis/lifecycle.ts b/src/apis/lifecycle.ts
index 32b05f3..55cb951 100644
--- a/src/apis/lifecycle.ts
+++ b/src/apis/lifecycle.ts
@@ -23,7 +23,7 @@ function injectHookOption(
   hook: string,
   val: Function
 ) {
-  const options = vm.$options as any
+  const options = vm.$options as Record<string, unknown>
   const mergeFn = Vue.config.optionMergeStrategies[hook]
   options[hook] = mergeFn(options[hook], wrapHookCall(vm, val))
 }
diff --git a/src/apis/nextTick.ts b/src/apis/nextTick.ts
index cb82c11..9c736ef 100644
--- a/src/apis/nextTick.ts
+++ b/src/apis/nextTick.ts
@@ -8,4 +8,4 @@ export const nextTick: NextTick = function nextTick(
   ...args: Parameters<NextTick>
 ) {
   return getVueConstructor()?.nextTick.apply(this, args)
-} as any
+}
diff --git a/src/apis/watch.ts b/src/apis/watch.ts
index 5073a60..8f8ce5d 100644
--- a/src/apis/watch.ts
+++ b/src/apis/watch.ts
@@ -255,7 +255,7 @@ function createWatcher(
           fn(...args)
         },
         flushMode as 'pre' | 'post'
-      )) as any as T
+      )) as unknown as T
   }
 
   // effect watch
@@ -471,7 +471,7 @@ function traverse(value: unknown, seen: Set<unknown> = new Set()) {
     })
   } else if (isPlainObject(value)) {
     for (const key in value) {
-      traverse((value as any)[key], seen)
+      traverse(value[key], seen)
     }
   }
   return value
diff --git a/src/mixin.ts b/src/mixin.ts
index 9bdc197..b0c3ae5 100644
--- a/src/mixin.ts
+++ b/src/mixin.ts
@@ -35,6 +35,9 @@ export function mixin(Vue: VueConstructor) {
     },
     updated(this: ComponentInstance) {
       updateTemplateRef(this)
+      if (this.$vnode?.context) {
+        updateTemplateRef(this.$vnode.context)
+      }
     },
   })
 
diff --git a/src/reactivity/reactive.ts b/src/reactivity/reactive.ts
index 2914d1e..7599ab6 100644
--- a/src/reactivity/reactive.ts
+++ b/src/reactivity/reactive.ts
@@ -171,12 +171,12 @@ export function createObserver() {
 }
 
 export function shallowReactive<T extends object = any>(obj: T): T
-export function shallowReactive(obj: any): any {
+export function shallowReactive(obj: any) {
   if (!isObject(obj)) {
     if (__DEV__) {
       warn('"shallowReactive()" must be called on an object.')
     }
-    return obj as any
+    return obj
   }
 
   if (
@@ -184,7 +184,7 @@ export function shallowReactive(obj: any): any {
     isRaw(obj) ||
     !Object.isExtensible(obj)
   ) {
-    return obj as any
+    return obj
   }
 
   const observed = observe(isArray(obj) ? [] : {})
@@ -233,7 +233,7 @@ export function reactive<T extends object>(obj: T): UnwrapRef<T> {
     if (__DEV__) {
       warn('"reactive()" must be called on an object.')
     }
-    return obj as any
+    return obj
   }
 
   if (
diff --git a/src/reactivity/readonly.ts b/src/reactivity/readonly.ts
index 4c1d49d..3c7ebd9 100644
--- a/src/reactivity/readonly.ts
+++ b/src/reactivity/readonly.ts
@@ -44,6 +44,9 @@ type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>
 export function readonly<T extends object>(
   target: T
 ): DeepReadonly<UnwrapNestedRefs<T>> {
+  if (__DEV__ && !isObject(target)) {
+    warn(`value cannot be made reactive: ${String(target)}`)
+  }
   return target as any
 }
 
diff --git a/src/reactivity/ref.ts b/src/reactivity/ref.ts
index 32c187d..14c7b65 100644
--- a/src/reactivity/ref.ts
+++ b/src/reactivity/ref.ts
@@ -122,7 +122,7 @@ export function toRefs<T extends object>(obj: T): ToRefs<T> {
   if (__DEV__ && !isReactive(obj)) {
     warn(`toRefs() expects a reactive object but received a plain one.`)
   }
-  if (!isPlainObject(obj)) return obj as any
+  if (!isPlainObject(obj)) return obj
 
   const ret: any = {}
   for (const key in obj) {
diff --git a/src/runtimeContext.ts b/src/runtimeContext.ts
index 64e2a65..bfdd68d 100644
--- a/src/runtimeContext.ts
+++ b/src/runtimeContext.ts
@@ -168,7 +168,7 @@ function toVue3ComponentInstance(
     return instanceMapCache.get(vue2Instance)!
   }
 
-  const instance: ComponentInternalInstance = ({
+  const instance: ComponentInternalInstance = {
     proxy: vue2Instance,
     update: vue2Instance.$forceUpdate,
     uid: vue2Instance._uid,
@@ -177,8 +177,8 @@ function toVue3ComponentInstance(
     emit: vue2Instance.$emit.bind(vue2Instance),
 
     parent: null,
-    root: null as any,
-  } as unknown) as ComponentInternalInstance
+    root: null!, // to be immediately set
+  } as unknown as ComponentInternalInstance
 
   // map vm.$props =
   const instanceProps = [


Test patch:
diff --git a/test/templateRefs.spec.js b/test/templateRefs.spec.js
index 10bbc56..f41ec84 100644
--- a/test/templateRefs.spec.js
+++ b/test/templateRefs.spec.js
@@ -1,5 +1,5 @@
 const Vue = require('vue/dist/vue.common.js')
-const { ref, watchEffect } = require('../src')
+const { ref, watchEffect, nextTick } = require('../src')
 
 describe('ref', () => {
   it('should work', (done) => {
@@ -69,6 +69,50 @@ describe('ref', () => {
       .then(done)
   })
 
+  // #296
+  it('should dynamically update refs in context', async () => {
+    const vm = new Vue({
+      setup() {
+        const barRef = ref(null)
+        return {
+          barRef,
+        }
+      },
+      template: `<div>
+        <foo><bar ref="barRef"/></foo>
+      </div>`,
+      components: {
+        bar: {
+          setup() {
+            const name = ref('bar')
+            return {
+              name,
+            }
+          },
+          template: '<div> {{name}} </div>',
+        },
+        foo: {
+          setup() {
+            const showSlot = ref(false)
+            const setShow = () => {
+              showSlot.value = true
+            }
+            return {
+              setShow,
+              showSlot,
+            }
+          },
+          template: `<div> <slot v-if="showSlot"></slot> </div>`,
+        },
+      },
+    }).$mount()
+    await nextTick()
+    //@ts-ignore
+    vm.$children[0].setShow()
+    await nextTick()
+    //@ts-ignore
+    expect(vm.$refs.barRef).toBe(vm.barRef)
+  })
   // TODO: how ?
   // it('work with createElement', () => {
   //   let root;
diff --git a/test/v3/reactivity/readonly.spec.ts b/test/v3/reactivity/readonly.spec.ts
index a645715..ebb513a 100644
--- a/test/v3/reactivity/readonly.spec.ts
+++ b/test/v3/reactivity/readonly.spec.ts
@@ -6,6 +6,7 @@ import {
   reactive,
   watch,
   nextTick,
+  readonly,
 } from '../../../src'
 
 const Vue = require('vue/dist/vue.common.js')
@@ -386,6 +387,9 @@ describe('reactivity/readonly', () => {
       // @ts-ignore
       shallowReadonly(25)
       expect(`value cannot be made reactive: 25`).toHaveBeenWarned()
+      // @ts-ignore
+      readonly(25)
+      expect(`value cannot be made reactive: 25`).toHaveBeenWarned()
     })
 
     // #669