import { isRef } from 'vue'; import type { Ref, ShallowUnwrapRef } from 'vue'; export interface ExtendRefOptions { /** * Whether the extended properties are enumerable. * * @default false */ enumerable?: boolean; /** * Whether to unwrap (auto-`.value`) extended properties that are themselves refs. * * @default true */ unwrap?: Unwrap; } export type ExtendRefReturn, Extend extends object, Unwrap extends boolean> = Unwrap extends false ? (R & ShallowUnwrapRef) : (R & Extend); /** * @name extendRef * @category Reactivity * @description Attach extra (optionally reactive) attributes to a ref while keeping it a usable ref. * * @param {Ref} ref The ref to extend * @param {object} extend The properties to attach; ref-valued props are unwrapped by default * @param {ExtendRefOptions} [options={}] `enumerable` (default `false`) and `unwrap` (default `true`) * @returns {Ref & Extend} The same ref instance, now carrying the extended properties * * @example * const myRef = ref('content'); * const extended = extendRef(myRef, { foo: 'bar' }); * extended.value; // 'content' * extended.foo; // 'bar' * * @example * // reactive extension: unwrapped two-way by default * const count = ref(0); * const extended = extendRef(count, { double: computed(() => count.value * 2) }); * extended.double; // 0 (no .value needed) * * @example * // keep refs as refs with unwrap: false * const extended = extendRef(ref(0), { inner: ref(1) }, { unwrap: false }); * extended.inner.value; // 1 * * @since 0.0.14 */ export function extendRef, Extend extends object, Options extends ExtendRefOptions>(ref: R, extend: Extend, options: Options): R & ShallowUnwrapRef; export function extendRef, Extend extends object, Options extends ExtendRefOptions>(ref: R, extend: Extend, options?: Options): R & Extend; export function extendRef, Extend extends object>( ref: R, extend: Extend, options: ExtendRefOptions = {}, ): R & Extend { const { enumerable = false, unwrap = true } = options; for (const key in extend) { if (key === 'value') continue; const value = extend[key]; if (unwrap && isRef(value)) { Object.defineProperty(ref, key, { get() { return value.value; }, set(v) { value.value = v; }, enumerable, configurable: true, }); } else { Object.defineProperty(ref, key, { value, enumerable, configurable: true, writable: true, }); } } return ref as R & Extend; }