Skip to content

Commit 302c47a

Browse files
authored
fix(types): use private branding for shallowReactive (#14641)
fix #14638 fix shallowReactive type regressions introduced by #14493
1 parent e20ddb0 commit 302c47a

File tree

4 files changed

+50
-19
lines changed

4 files changed

+50
-19
lines changed

packages-private/dts-test/reactivity.test-d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,9 @@ describe('shallowReactive marker should not leak into value unions', () => {
140140
const value = {} as (typeof state)[keyof typeof state]
141141
expectType<string>(value.title)
142142
})
143+
144+
describe('shallowReactive type should accept plain object assignment', () => {
145+
const shallow = shallowReactive({ a: 1, b: 2 })
146+
let values: typeof shallow
147+
values = { a: 1, b: 2 }
148+
})

packages-private/dts-test/ref.test-d.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,12 @@ expectType<undefined>(p2.u)
321321
expectType<Ref<string>>(p2.obj.k)
322322
expectType<{ name: string } | null>(p2.union)
323323

324+
const r3 = shallowReactive({
325+
n: ref(1),
326+
})
327+
const p3 = proxyRefs(r3)
328+
expectType<Ref<number>>(p3.n)
329+
324330
// toRef and toRefs
325331
{
326332
const obj: {
@@ -437,6 +443,15 @@ describe('shallow reactive in reactive', () => {
437443
expectType<number>(foo.value.a.b.value)
438444
})
439445

446+
describe('shallow reactive collection in reactive', () => {
447+
const baz = reactive({
448+
foo: shallowReactive(new Map([['a', ref(42)]])),
449+
})
450+
451+
const foo = toRef(baz, 'foo')
452+
expectType<Ref<number> | undefined>(foo.value.get('a'))
453+
})
454+
440455
describe('shallow ref in reactive', () => {
441456
const x = reactive({
442457
foo: shallowRef({

packages/reactivity/src/reactive.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,16 @@ export function reactive(target: object) {
104104
)
105105
}
106106

107-
export declare const ShallowReactiveMarker: unique symbol
107+
// Use a private class brand instead of a marker property so shallow-reactive
108+
// types remain distinguishable in `UnwrapRef` without leaking the brand into
109+
// `keyof`/indexed access types or requiring the property for plain assignment.
110+
declare class ShallowReactiveBrandClass {
111+
private __shallowReactiveBrand?: never
112+
}
113+
114+
export type ShallowReactiveBrand = ShallowReactiveBrandClass
108115

109-
export type ShallowReactive<T> = T & { [ShallowReactiveMarker]: never }
116+
export type ShallowReactive<T> = T & ShallowReactiveBrand
110117

111118
/**
112119
* Shallow version of {@link reactive}.

packages/reactivity/src/ref.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import { Dep, getDepFromReactive } from './dep'
1111
import {
1212
type Builtin,
13-
type ShallowReactiveMarker,
13+
type ShallowReactiveBrand,
1414
type Target,
1515
isProxy,
1616
isReactive,
@@ -539,9 +539,11 @@ function propertyToRef(
539539
*/
540540
export interface RefUnwrapBailTypes {}
541541

542-
export type ShallowUnwrapRef<T> = {
543-
[K in keyof T]: DistributeRef<T[K]>
544-
}
542+
export type ShallowUnwrapRef<T> = T extends ShallowReactiveBrand
543+
? T
544+
: {
545+
[K in keyof T]: DistributeRef<T[K]>
546+
}
545547

546548
type DistributeRef<T> = T extends Ref<infer V, unknown> ? V : T
547549

@@ -558,19 +560,20 @@ export type UnwrapRefSimple<T> = T extends
558560
| RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
559561
| { [RawSymbol]?: true }
560562
? T
561-
: T extends Map<infer K, infer V>
562-
? Map<K, UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Map<any, any>>>
563-
: T extends WeakMap<infer K, infer V>
564-
? WeakMap<K, UnwrapRefSimple<V>> &
565-
UnwrapRef<Omit<T, keyof WeakMap<any, any>>>
566-
: T extends Set<infer V>
567-
? Set<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Set<any>>>
568-
: T extends WeakSet<infer V>
569-
? WeakSet<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof WeakSet<any>>>
570-
: T extends ReadonlyArray<any>
571-
? { [K in keyof T]: UnwrapRefSimple<T[K]> }
572-
: T extends object & { [ShallowReactiveMarker]: never }
573-
? T
563+
: T extends ShallowReactiveBrand
564+
? T
565+
: T extends Map<infer K, infer V>
566+
? Map<K, UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Map<any, any>>>
567+
: T extends WeakMap<infer K, infer V>
568+
? WeakMap<K, UnwrapRefSimple<V>> &
569+
UnwrapRef<Omit<T, keyof WeakMap<any, any>>>
570+
: T extends Set<infer V>
571+
? Set<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Set<any>>>
572+
: T extends WeakSet<infer V>
573+
? WeakSet<UnwrapRefSimple<V>> &
574+
UnwrapRef<Omit<T, keyof WeakSet<any>>>
575+
: T extends ReadonlyArray<any>
576+
? { [K in keyof T]: UnwrapRefSimple<T[K]> }
574577
: T extends object
575578
? {
576579
[P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>

0 commit comments

Comments
 (0)