-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
/
ref.ts
102 lines (93 loc) · 2.84 KB
/
ref.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { track, trigger } from './effect'
import { TrackOpTypes, TriggerOpTypes } from './operations'
import { isObject } from '@vue/shared'
import { reactive, isReactive } from './reactive'
import { ComputedRef } from './computed'
import { CollectionTypes } from './collectionHandlers'
const isRefSymbol = Symbol()
export interface Ref<T = any> {
// This field is necessary to allow TS to differentiate a Ref from a plain
// object that happens to have a "value" field.
// However, checking a symbol on an arbitrary object is much slower than
// checking a plain property, so we use a _isRef plain property for isRef()
// check in the actual implementation.
// The reason for not just declaring _isRef in the interface is because we
// don't want this internal field to leak into userland autocompletion -
// a private symbol, on the other hand, achieves just that.
[isRefSymbol]: true
value: UnwrapRef<T>
}
const convert = <T extends unknown>(val: T): T =>
isObject(val) ? reactive(val) : val
export function isRef<T>(r: Ref<T> | T): r is Ref<T>
export function isRef(r: any): r is Ref {
return r ? r._isRef === true : false
}
export function ref<T extends Ref>(raw: T): T
export function ref<T>(raw: T): Ref<T>
export function ref<T = any>(): Ref<T>
export function ref(raw?: unknown) {
if (isRef(raw)) {
return raw
}
raw = convert(raw)
const r = {
_isRef: true,
get value() {
track(r, TrackOpTypes.GET, 'value')
return raw
},
set value(newVal) {
raw = convert(newVal)
trigger(
r,
TriggerOpTypes.SET,
'value',
__DEV__ ? { newValue: newVal } : void 0
)
}
}
return r
}
export function toRefs<T extends object>(
object: T
): { [K in keyof T]: Ref<T[K]> } {
if (__DEV__ && !isReactive(object)) {
console.warn(`toRefs() expects a reactive object but received a plain one.`)
}
const ret: any = {}
for (const key in object) {
ret[key] = toProxyRef(object, key)
}
return ret
}
function toProxyRef<T extends object, K extends keyof T>(
object: T,
key: K
): Ref<T[K]> {
return {
_isRef: true,
get value(): any {
return object[key]
},
set value(newVal) {
object[key] = newVal
}
} as any
}
type UnwrapArray<T> = { [P in keyof T]: UnwrapRef<T[P]> }
// Recursively unwraps nested value bindings.
export type UnwrapRef<T> = {
cRef: T extends ComputedRef<infer V> ? UnwrapRef<V> : T
ref: T extends Ref<infer V> ? UnwrapRef<V> : T
array: T extends Array<infer V> ? Array<UnwrapRef<V>> & UnwrapArray<T> : T
object: { [K in keyof T]: UnwrapRef<T[K]> }
}[T extends ComputedRef<any>
? 'cRef'
: T extends Ref
? 'ref'
: T extends Array<any>
? 'array'
: T extends Function | CollectionTypes
? 'ref' // bail out on types that shouldn't be unwrapped
: T extends object ? 'object' : 'ref']