Skip to content

Commit 030d02c

Browse files
committed
feat: proxyRefs method and ShallowUnwrapRefs type
BREAKING CHANGE: template auto ref unwrapping are now applied shallowly, i.e. only at the root level. See vuejs/core#1682 for more details.
1 parent 91c6106 commit 030d02c

File tree

9 files changed

+135
-139
lines changed

9 files changed

+135
-139
lines changed

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,17 @@ app2.component('Bar', Bar) // equivalent to Vue.use('Bar', Bar)
392392
393393
</details>
394394

395+
### props
396+
<details>
397+
<summary>
398+
⚠️ <code>toRefs(props.foo.bar)</code> will incorrectly warn when acessing nested levels of props.
399+
⚠️ <code>isReactive(props.foo.bar)</code> will return false.
400+
</summary>
401+
TODO add description
402+
</details>
403+
404+
405+
395406
### Missing APIs
396407

397408
The following APIs introduced in Vue 3 are not available in this plugin.

src/apis/state.ts

+2
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,6 @@ export {
1818
UnwrapRef,
1919
isReadonly,
2020
shallowReadonly,
21+
proxyRefs,
22+
ShallowUnwrapRef,
2123
} from '../reactivity'

src/component/componentProxy.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ExtractPropTypes } from './componentProps'
2-
import { UnwrapRef } from '..'
2+
import { ShallowUnwrapRef } from '..'
33
import { Data } from './common'
44

55
import Vue, {
@@ -28,7 +28,7 @@ export type ComponentRenderProxy<
2828
$props: Readonly<P & PublicProps>
2929
$attrs: Data
3030
} & Readonly<P> &
31-
UnwrapRef<B> &
31+
ShallowUnwrapRef<B> &
3232
D &
3333
M &
3434
ExtractComputedReturns<C> &
@@ -38,7 +38,7 @@ export type ComponentRenderProxy<
3838
type VueConstructorProxy<PropsOptions, RawBindings> = VueConstructor & {
3939
new (...args: any[]): ComponentRenderProxy<
4040
ExtractPropTypes<PropsOptions>,
41-
UnwrapRef<RawBindings>,
41+
ShallowUnwrapRef<RawBindings>,
4242
ExtractPropTypes<PropsOptions, false>
4343
>
4444
}
@@ -55,7 +55,7 @@ export type VueProxy<
5555
Methods = DefaultMethods<Vue>
5656
> = Vue2ComponentOptions<
5757
Vue,
58-
UnwrapRef<RawBindings> & Data,
58+
ShallowUnwrapRef<RawBindings> & Data,
5959
Methods,
6060
Computed,
6161
PropsOptions,

src/mixin.ts

+5-13
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,7 @@ import {
55
SetupFunction,
66
Data,
77
} from './component'
8-
import {
9-
isRef,
10-
isReactive,
11-
markRaw,
12-
unwrapRefProxy,
13-
markReactive,
14-
} from './reactivity'
8+
import { isRef, isReactive, markRaw, markReactive } from './reactivity'
159
import { isPlainObject, assert, proxy, warn, isFunction } from './utils'
1610
import { ref } from './apis'
1711
import vmStateManager from './utils/vmStateManager'
@@ -79,7 +73,7 @@ export function mixin(Vue: VueConstructor) {
7973
const setup = vm.$options.setup!
8074
const ctx = createSetupContext(vm)
8175

82-
// mark props as reactive
76+
// mark props
8377
markReactive(props)
8478

8579
// resolve scopedSlots and slots to functions
@@ -105,6 +99,8 @@ export function mixin(Vue: VueConstructor) {
10599
const bindingObj = binding
106100
vmStateManager.set(vm, 'rawBindings', binding)
107101

102+
// binding = proxyRefs(binding);
103+
108104
Object.keys(binding).forEach((name) => {
109105
let bindingValue = bindingObj[name]
110106
// only make primitive value reactive
@@ -116,12 +112,8 @@ export function mixin(Vue: VueConstructor) {
116112
if (isFunction(bindingValue)) {
117113
bindingValue = bindingValue.bind(vm)
118114
}
119-
// unwrap all ref properties
120-
const unwrapped = unwrapRefProxy(bindingValue)
121-
// mark the object as reactive
122-
markReactive(unwrapped)
123115
// a non-reactive should not don't get reactivity
124-
bindingValue = ref(markRaw(unwrapped))
116+
bindingValue = ref(markRaw(bindingValue))
125117
}
126118
}
127119
asVmProperty(vm, name, bindingValue)

src/reactivity/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export {
2121
unref,
2222
shallowRef,
2323
triggerRef,
24+
proxyRefs,
25+
ShallowUnwrapRef,
2426
} from './ref'
2527
export { set } from './set'
26-
export { unwrapRefProxy } from './unwrap'

src/reactivity/ref.ts

+37-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { Data } from '../component'
22
import { RefKey, ReadonlyIdentifierKey } from '../utils/symbols'
33
import { proxy, isPlainObject, warn } from '../utils'
44
import { reactive, isReactive, shallowReactive } from './reactive'
5-
import { ComputedRef } from '../apis/computed'
65

76
declare const _refBrand: unique symbol
87
export interface Ref<T = any> {
@@ -22,16 +21,18 @@ type WeakCollections = WeakMap<any, any> | WeakSet<any>
2221
// RelativePath extends object -> true
2322
type BaseTypes = string | number | boolean | Node | Window
2423

25-
export type UnwrapRef<T> = T extends ComputedRef<infer V>
26-
? UnwrapRefSimple<V>
27-
: T extends Ref<infer V>
24+
export type ShallowUnwrapRef<T> = {
25+
[K in keyof T]: T[K] extends Ref<infer V> ? V : T[K]
26+
}
27+
28+
export type UnwrapRef<T> = T extends Ref<infer V>
2829
? UnwrapRefSimple<V>
2930
: UnwrapRefSimple<T>
3031

3132
type UnwrapRefSimple<T> = T extends Function | CollectionTypes | BaseTypes | Ref
3233
? T
3334
: T extends Array<any>
34-
? T
35+
? { [K in keyof T]: UnwrapRefSimple<T[K]> }
3536
: T extends object
3637
? UnwrappedObject<T>
3738
: T
@@ -50,6 +51,7 @@ type SymbolExtract<T> = (T extends { [Symbol.asyncIterator]: infer V }
5051
: {}) &
5152
(T extends { [Symbol.iterator]: infer V } ? { [Symbol.iterator]: V } : {}) &
5253
(T extends { [Symbol.match]: infer V } ? { [Symbol.match]: V } : {}) &
54+
(T extends { [Symbol.matchAll]: infer V } ? { [Symbol.matchAll]: V } : {}) &
5355
(T extends { [Symbol.replace]: infer V } ? { [Symbol.replace]: V } : {}) &
5456
(T extends { [Symbol.search]: infer V } ? { [Symbol.search]: V } : {}) &
5557
(T extends { [Symbol.species]: infer V } ? { [Symbol.species]: V } : {}) &
@@ -185,3 +187,33 @@ export function triggerRef(value: any) {
185187

186188
value.value = value.value
187189
}
190+
191+
export function proxyRefs<T extends object>(
192+
objectWithRefs: T
193+
): ShallowUnwrapRef<T> {
194+
if (isReactive(objectWithRefs)) {
195+
//@ts-ignore
196+
return objectWithRefs
197+
}
198+
const value: Record<string, any> = reactive({ [RefKey]: objectWithRefs })
199+
200+
for (const key of Object.keys(objectWithRefs)) {
201+
proxy(value, key, {
202+
get() {
203+
if (isRef(value[key])) {
204+
return value[key].value
205+
}
206+
return value[key]
207+
},
208+
set(v: unknown) {
209+
if (isRef(value[key])) {
210+
return (value[key].value = unref(v))
211+
}
212+
value[key] = unref(v)
213+
},
214+
})
215+
}
216+
217+
// @ts-ignore
218+
return value
219+
}

src/reactivity/unwrap.ts

-56
This file was deleted.

test-dts/defineComponent.test-d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ describe('with object props', () => {
155155

156156
// assert setup context unwrapping
157157
expectType<number>(this.c)
158-
expectType<string>(this.d.e)
158+
expectType<string>(this.d.e.value)
159159
expectType<GT>(this.f.g)
160160

161161
// setup context properties should be mutable

0 commit comments

Comments
 (0)