import { track, trigger } from './effect' import { OperationTypes } from './operations' import { isObject } from '@vue/shared' import { reactive } from './reactive' export const refSymbol = Symbol(__DEV__ ? 'refSymbol' : undefined) export interface Ref { [refSymbol]: true value: UnwrapNestedRefs } export type UnwrapNestedRefs = T extends Ref ? T : UnwrapRef const convert = (val: any): any => (isObject(val) ? reactive(val) : val) export function ref(raw: T): Ref { raw = convert(raw) const v = { [refSymbol]: true, get value() { track(v, OperationTypes.GET, '') return raw }, set value(newVal) { raw = convert(newVal) trigger(v, OperationTypes.SET, '') } } return v as Ref } export function isRef(v: any): v is Ref { return v ? v[refSymbol] === true : false } export function toRefs( object: T ): { [K in keyof T]: Ref } { const ret: any = {} for (const key in object) { ret[key] = toProxyRef(object, key) } return ret } function toProxyRef( object: T, key: K ): Ref { const v = { [refSymbol]: true, get value() { return object[key] }, set value(newVal) { object[key] = newVal } } return v as Ref } type BailTypes = | Function | Map | Set | WeakMap | WeakSet // Recursively unwraps nested value bindings. export type UnwrapRef = { ref: T extends Ref ? UnwrapRef : T array: T extends Array ? Array> : T object: { [K in keyof T]: UnwrapRef } stop: T }[T extends Ref ? 'ref' : T extends Array ? 'array' : T extends BailTypes ? 'stop' // bail out on types that shouldn't be unwrapped : T extends object ? 'object' : 'stop']