Просмотр исходного кода

fix(types): use private branding for shallowReactive (#14641)

fix #14638
fix shallowReactive type regressions introduced by #14493
edison 2 недель назад
Родитель
Сommit
302c47a499

+ 6 - 0
packages-private/dts-test/reactivity.test-d.ts

@@ -140,3 +140,9 @@ describe('shallowReactive marker should not leak into value unions', () => {
   const value = {} as (typeof state)[keyof typeof state]
   expectType<string>(value.title)
 })
+
+describe('shallowReactive type should accept plain object assignment', () => {
+  const shallow = shallowReactive({ a: 1, b: 2 })
+  let values: typeof shallow
+  values = { a: 1, b: 2 }
+})

+ 15 - 0
packages-private/dts-test/ref.test-d.ts

@@ -321,6 +321,12 @@ expectType<undefined>(p2.u)
 expectType<Ref<string>>(p2.obj.k)
 expectType<{ name: string } | null>(p2.union)
 
+const r3 = shallowReactive({
+  n: ref(1),
+})
+const p3 = proxyRefs(r3)
+expectType<Ref<number>>(p3.n)
+
 // toRef and toRefs
 {
   const obj: {
@@ -437,6 +443,15 @@ describe('shallow reactive in reactive', () => {
   expectType<number>(foo.value.a.b.value)
 })
 
+describe('shallow reactive collection in reactive', () => {
+  const baz = reactive({
+    foo: shallowReactive(new Map([['a', ref(42)]])),
+  })
+
+  const foo = toRef(baz, 'foo')
+  expectType<Ref<number> | undefined>(foo.value.get('a'))
+})
+
 describe('shallow ref in reactive', () => {
   const x = reactive({
     foo: shallowRef({

+ 9 - 2
packages/reactivity/src/reactive.ts

@@ -104,9 +104,16 @@ export function reactive(target: object) {
   )
 }
 
-export declare const ShallowReactiveMarker: unique symbol
+// Use a private class brand instead of a marker property so shallow-reactive
+// types remain distinguishable in `UnwrapRef` without leaking the brand into
+// `keyof`/indexed access types or requiring the property for plain assignment.
+declare class ShallowReactiveBrandClass {
+  private __shallowReactiveBrand?: never
+}
+
+export type ShallowReactiveBrand = ShallowReactiveBrandClass
 
-export type ShallowReactive<T> = T & { [ShallowReactiveMarker]: never }
+export type ShallowReactive<T> = T & ShallowReactiveBrand
 
 /**
  * Shallow version of {@link reactive}.

+ 20 - 17
packages/reactivity/src/ref.ts

@@ -10,7 +10,7 @@ import {
 import { Dep, getDepFromReactive } from './dep'
 import {
   type Builtin,
-  type ShallowReactiveMarker,
+  type ShallowReactiveBrand,
   type Target,
   isProxy,
   isReactive,
@@ -539,9 +539,11 @@ function propertyToRef(
  */
 export interface RefUnwrapBailTypes {}
 
-export type ShallowUnwrapRef<T> = {
-  [K in keyof T]: DistributeRef<T[K]>
-}
+export type ShallowUnwrapRef<T> = T extends ShallowReactiveBrand
+  ? T
+  : {
+      [K in keyof T]: DistributeRef<T[K]>
+    }
 
 type DistributeRef<T> = T extends Ref<infer V, unknown> ? V : T
 
@@ -558,19 +560,20 @@ export type UnwrapRefSimple<T> = T extends
   | RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
   | { [RawSymbol]?: true }
   ? T
-  : T extends Map<infer K, infer V>
-    ? Map<K, UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Map<any, any>>>
-    : T extends WeakMap<infer K, infer V>
-      ? WeakMap<K, UnwrapRefSimple<V>> &
-          UnwrapRef<Omit<T, keyof WeakMap<any, any>>>
-      : T extends Set<infer V>
-        ? Set<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Set<any>>>
-        : T extends WeakSet<infer V>
-          ? WeakSet<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof WeakSet<any>>>
-          : T extends ReadonlyArray<any>
-            ? { [K in keyof T]: UnwrapRefSimple<T[K]> }
-            : T extends object & { [ShallowReactiveMarker]: never }
-              ? T
+  : T extends ShallowReactiveBrand
+    ? T
+    : T extends Map<infer K, infer V>
+      ? Map<K, UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Map<any, any>>>
+      : T extends WeakMap<infer K, infer V>
+        ? WeakMap<K, UnwrapRefSimple<V>> &
+            UnwrapRef<Omit<T, keyof WeakMap<any, any>>>
+        : T extends Set<infer V>
+          ? Set<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Set<any>>>
+          : T extends WeakSet<infer V>
+            ? WeakSet<UnwrapRefSimple<V>> &
+                UnwrapRef<Omit<T, keyof WeakSet<any>>>
+            : T extends ReadonlyArray<any>
+              ? { [K in keyof T]: UnwrapRefSimple<T[K]> }
               : T extends object
                 ? {
                     [P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>