]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(types): fix ref unwrapping type inference for nested shallowReactive & shallowRef
authorEvan You <yyx990803@gmail.com>
Sat, 9 Oct 2021 21:51:09 +0000 (17:51 -0400)
committerEvan You <yyx990803@gmail.com>
Sat, 9 Oct 2021 21:51:15 +0000 (17:51 -0400)
fix #4771

packages/reactivity/src/index.ts
packages/reactivity/src/reactive.ts
packages/reactivity/src/ref.ts
packages/runtime-core/src/index.ts
test-dts/ref.test-d.ts

index b15f4d807be3d2047d66282232f7c0db1a50371a..d608e8b8f3face22d8fa31238d5df15cf686387a 100644 (file)
@@ -27,6 +27,7 @@ export {
   toRaw,
   ReactiveFlags,
   DeepReadonly,
+  ShallowReactive,
   UnwrapNestedRefs
 } from './reactive'
 export {
index 9cce2ea0e640b92c51489bc1876d3270790035a3..b56cb3627b5b8c0bc96b7ad102893a840f87f73d 100644 (file)
@@ -99,12 +99,18 @@ export function reactive(target: object) {
   )
 }
 
+export declare const ShallowReactiveMarker: unique symbol
+
+export type ShallowReactive<T> = T & { [ShallowReactiveMarker]?: true }
+
 /**
  * Return a shallowly-reactive copy of the original object, where only the root
  * level properties are reactive. It also does not auto-unwrap refs (even at the
  * root level).
  */
-export function shallowReactive<T extends object>(target: T): T {
+export function shallowReactive<T extends object>(
+  target: T
+): ShallowReactive<T> {
   return createReactiveObject(
     target,
     false,
index f33375824fef6ea3320b29a565d1584acc92fa78..d1cc17eabfe7cd9f4be7e53c3051d828c064c743 100644 (file)
@@ -1,7 +1,13 @@
 import { isTracking, trackEffects, triggerEffects } from './effect'
 import { TrackOpTypes, TriggerOpTypes } from './operations'
 import { isArray, hasChanged } from '@vue/shared'
-import { isProxy, toRaw, isReactive, toReactive } from './reactive'
+import {
+  isProxy,
+  toRaw,
+  isReactive,
+  toReactive,
+  ShallowReactiveMarker
+} from './reactive'
 import { CollectionTypes } from './collectionHandlers'
 import { createDep, Dep } from './dep'
 
@@ -74,11 +80,15 @@ export function ref(value?: unknown) {
   return createRef(value, false)
 }
 
+declare const ShallowRefMarker: unique symbol
+
+type ShallowRef<T = any> = Ref<T> & { [ShallowRefMarker]?: true }
+
 export function shallowRef<T extends object>(
   value: T
-): T extends Ref ? T : Ref<T>
-export function shallowRef<T>(value: T): Ref<T>
-export function shallowRef<T = any>(): Ref<T | undefined>
+): T extends Ref ? T : ShallowRef<T>
+export function shallowRef<T>(value: T): ShallowRef<T>
+export function shallowRef<T = any>(): ShallowRef<T | undefined>
 export function shallowRef(value?: unknown) {
   return createRef(value, true)
 }
@@ -215,6 +225,7 @@ class ObjectRefImpl<T extends object, K extends keyof T> {
 }
 
 export type ToRef<T> = [T] extends [Ref] ? T : Ref<T>
+
 export function toRef<T extends object, K extends keyof T>(
   object: T,
   key: K
@@ -258,7 +269,9 @@ export type ShallowUnwrapRef<T> = {
     : T[K]
 }
 
-export type UnwrapRef<T> = T extends Ref<infer V>
+export type UnwrapRef<T> = T extends ShallowRef<infer V>
+  ? V
+  : T extends Ref<infer V>
   ? UnwrapRefSimple<V>
   : UnwrapRefSimple<T>
 
@@ -271,7 +284,7 @@ export type UnwrapRefSimple<T> = T extends
   ? T
   : T extends Array<any>
   ? { [K in keyof T]: UnwrapRefSimple<T[K]> }
-  : T extends object
+  : T extends object & { [ShallowReactiveMarker]?: never }
   ? {
       [P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>
     }
index b1a594263c1fef9188dec825da87ce14ca3ef26f..ff0aeb90df4c26777bf338ab1e2107013f66da4c 100644 (file)
@@ -161,7 +161,8 @@ export {
   UnwrapRef,
   ShallowUnwrapRef,
   WritableComputedOptions,
-  DeepReadonly
+  DeepReadonly,
+  ShallowReactive
 } from '@vue/reactivity'
 export {
   WatchEffect,
index 8129d6182e91e2ccc8ecf68490210b164ad6ff0a..d4462db9e39e272f944eeb99cfe6423cd57ef311 100644 (file)
@@ -239,13 +239,65 @@ function testUnrefGenerics<T>(p: T | Ref<T>) {
 testUnrefGenerics(1)
 
 // #4732
-const baz = shallowReactive({
-  foo: {
-    bar: ref(42)
-  }
+describe('ref in shallow reactive', () => {
+  const baz = shallowReactive({
+    foo: {
+      bar: ref(42)
+    }
+  })
+
+  const foo = toRef(baz, 'foo')
+
+  expectType<Ref<number>>(foo.value.bar)
+  expectType<number>(foo.value.bar.value)
+})
+
+// #4771
+describe('shallow reactive in reactive', () => {
+  const baz = reactive({
+    foo: shallowReactive({
+      a: {
+        b: ref(42)
+      }
+    })
+  })
+
+  const foo = toRef(baz, 'foo')
+
+  expectType<Ref<number>>(foo.value.a.b)
+  expectType<number>(foo.value.a.b.value)
 })
 
-const foo = toRef(baz, 'foo')
+describe('shallow ref in reactive', () => {
+  const x = reactive({
+    foo: shallowRef({
+      bar: {
+        baz: ref(123),
+        qux: reactive({
+          z: ref(123)
+        })
+      }
+    })
+  })
+
+  expectType<Ref<number>>(x.foo.bar.baz)
+  expectType<number>(x.foo.bar.qux.z)
+})
+
+describe('ref in shallow ref', () => {
+  const x = shallowRef({
+    a: ref(123)
+  })
 
-expectType<Ref<number>>(foo.value.bar)
-expectType<number>(foo.value.bar.value)
+  expectType<Ref<number>>(x.value.a)
+})
+
+describe('reactive in shallow ref', () => {
+  const x = shallowRef({
+    a: reactive({
+      b: ref(0)
+    })
+  })
+
+  expectType<number>(x.value.a.b)
+})