import { effect, ReactiveEffect, trigger, track } from './effect' import { TriggerOpTypes, TrackOpTypes } from './operations' import { Ref, UnwrapRef } from './ref' import { isFunction, NOOP } from '@vue/shared' export interface ComputedRef extends WritableComputedRef { readonly value: UnwrapRef } export interface WritableComputedRef extends Ref { readonly effect: ReactiveEffect } export type ComputedGetter = (ctx?: any) => T export type ComputedSetter = (v: T) => void export interface WritableComputedOptions { get: ComputedGetter set: ComputedSetter } export function computed(getter: ComputedGetter): ComputedRef export function computed( options: WritableComputedOptions ): WritableComputedRef export function computed( getterOrOptions: ComputedGetter | WritableComputedOptions ) { let getter: ComputedGetter let setter: ComputedSetter if (isFunction(getterOrOptions)) { getter = getterOrOptions setter = __DEV__ ? () => { console.warn('Write operation failed: computed value is readonly') } : NOOP } else { getter = getterOrOptions.get setter = getterOrOptions.set } let dirty = true let value: T let computed: ComputedRef const runner = effect(getter, { lazy: true, // mark effect as computed so that it gets priority during trigger computed: true, scheduler: () => { if (!dirty) { dirty = true trigger(computed, TriggerOpTypes.SET, 'value') } } }) computed = { _isRef: true, // expose effect so computed can be stopped effect: runner, get value() { if (dirty) { value = runner() dirty = false } track(computed, TrackOpTypes.GET, 'value') return value }, set value(newValue: T) { setter(newValue) } } as any return computed }