import { track, trigger } from './effect' import { TrackOpTypes, TriggerOpTypes } from './operations' import { isObject, hasChanged } from '@vue/shared' import { reactive, isProxy, toRaw } from './reactive' import { ComputedRef } from './computed' import { CollectionTypes } from './collectionHandlers' declare const RefSymbol: unique symbol export interface Ref { /** * Type differentiator only. * We need this to be in public d.ts but don't want it to show up in IDE * autocomplete, so we use a private Symbol instead. */ [RefSymbol]: true value: T } export type ToRefs = { [K in keyof T]: Ref } const convert = (val: T): T => isObject(val) ? reactive(val) : val export function isRef(r: Ref | unknown): r is Ref export function isRef(r: any): r is Ref { return r ? r.__v_isRef === true : false } export function ref( value: T ): T extends Ref ? T : Ref> export function ref(value: T): Ref> export function ref(): Ref export function ref(value?: unknown) { return createRef(value) } export function shallowRef(value: T): T extends Ref ? T : Ref export function shallowRef(): Ref export function shallowRef(value?: unknown) { return createRef(value, true) } function createRef(rawValue: unknown, shallow = false) { if (isRef(rawValue)) { return rawValue } let value = shallow ? rawValue : convert(rawValue) const r = { __v_isRef: true, get value() { track(r, TrackOpTypes.GET, 'value') return value }, set value(newVal) { if (hasChanged(toRaw(newVal), rawValue)) { rawValue = newVal value = shallow ? newVal : convert(newVal) trigger( r, TriggerOpTypes.SET, 'value', __DEV__ ? { newValue: newVal } : void 0 ) } } } return r } export function triggerRef(ref: Ref) { trigger( ref, TriggerOpTypes.SET, 'value', __DEV__ ? { newValue: ref.value } : void 0 ) } export function unref(ref: T): T extends Ref ? V : T { return isRef(ref) ? (ref.value as any) : ref } export type CustomRefFactory = ( track: () => void, trigger: () => void ) => { get: () => T set: (value: T) => void } export function customRef(factory: CustomRefFactory): Ref { const { get, set } = factory( () => track(r, TrackOpTypes.GET, 'value'), () => trigger(r, TriggerOpTypes.SET, 'value') ) const r = { __v_isRef: true, get value() { return get() }, set value(v) { set(v) } } return r as any } export function toRefs(object: T): ToRefs { if (__DEV__ && !isProxy(object)) { console.warn(`toRefs() expects a reactive object but received a plain one.`) } const ret: any = {} for (const key in object) { ret[key] = toRef(object, key) } return ret } export function toRef( object: T, key: K ): Ref { return { __v_isRef: true, get value(): any { return object[key] }, set value(newVal) { object[key] = newVal } } as any } // corner case when use narrows type // Ex. type RelativePath = string & { __brand: unknown } // RelativePath extends object -> true type BaseTypes = string | number | boolean /** * This is a special exported interface for other packages to declare * additional types that should bail out for ref unwrapping. For example * \@vue/runtime-dom can declare it like so in its d.ts: * * ``` ts * declare module '@vue/reactivity' { * export interface RefUnwrapBailTypes { * runtimeDOMBailTypes: Node | Window * } * } * ``` * * Note that api-extractor somehow refuses to include `decalre module` * augmentations in its generated d.ts, so we have to manually append them * to the final generated d.ts in our build process. */ export interface RefUnwrapBailTypes {} export type UnwrapRef = T extends ComputedRef ? UnwrapRefSimple : T extends Ref ? UnwrapRefSimple : UnwrapRefSimple type UnwrapRefSimple = T extends | Function | CollectionTypes | BaseTypes | Ref | RefUnwrapBailTypes[keyof RefUnwrapBailTypes] ? T : T extends Array ? T : T extends object ? UnwrappedObject : T // Extract all known symbols from an object // when unwrapping Object the symbols are not `in keyof`, this should cover all the // known symbols type SymbolExtract = (T extends { [Symbol.asyncIterator]: infer V } ? { [Symbol.asyncIterator]: V } : {}) & (T extends { [Symbol.hasInstance]: infer V } ? { [Symbol.hasInstance]: V } : {}) & (T extends { [Symbol.isConcatSpreadable]: infer V } ? { [Symbol.isConcatSpreadable]: V } : {}) & (T extends { [Symbol.iterator]: infer V } ? { [Symbol.iterator]: V } : {}) & (T extends { [Symbol.match]: infer V } ? { [Symbol.match]: V } : {}) & (T extends { [Symbol.matchAll]: infer V } ? { [Symbol.matchAll]: V } : {}) & (T extends { [Symbol.replace]: infer V } ? { [Symbol.replace]: V } : {}) & (T extends { [Symbol.search]: infer V } ? { [Symbol.search]: V } : {}) & (T extends { [Symbol.species]: infer V } ? { [Symbol.species]: V } : {}) & (T extends { [Symbol.split]: infer V } ? { [Symbol.split]: V } : {}) & (T extends { [Symbol.toPrimitive]: infer V } ? { [Symbol.toPrimitive]: V } : {}) & (T extends { [Symbol.toStringTag]: infer V } ? { [Symbol.toStringTag]: V } : {}) & (T extends { [Symbol.unscopables]: infer V } ? { [Symbol.unscopables]: V } : {}) type UnwrappedObject = { [P in keyof T]: UnwrapRef } & SymbolExtract