wip: improve computed typing + test for setters

This commit is contained in:
Evan You 2019-08-21 12:01:05 -04:00
parent 8d99ab1ff8
commit 0aff144f93
6 changed files with 60 additions and 23 deletions

View File

@ -1,4 +1,4 @@
import { computed, reactive, effect, stop } from '../src' import { computed, reactive, effect, stop, ref } from '../src'
describe('reactivity/computed', () => { describe('reactivity/computed', () => {
it('should return updated value', () => { it('should return updated value', () => {
@ -123,6 +123,38 @@ describe('reactivity/computed', () => {
}) })
it('should support setter', () => { it('should support setter', () => {
// TODO const n = ref(1)
const plusOne = computed({
get: () => n.value + 1,
set: val => {
n.value = val - 1
}
})
expect(plusOne.value).toBe(2)
n.value++
expect(plusOne.value).toBe(3)
plusOne.value = 0
expect(n.value).toBe(-1)
})
it('should trigger effect w/ setter', () => {
const n = ref(1)
const plusOne = computed({
get: () => n.value + 1,
set: val => {
n.value = val - 1
}
})
let dummy
effect(() => {
dummy = n.value
})
expect(dummy).toBe(1)
plusOne.value = 0
expect(dummy).toBe(-1)
}) })
}) })

View File

@ -45,7 +45,7 @@ describe('reactivity/immutable', () => {
}) })
it('should not allow mutation', () => { it('should not allow mutation', () => {
const observed = immutable({ foo: 1, bar: { baz: 2 } }) const observed: any = immutable({ foo: 1, bar: { baz: 2 } })
observed.foo = 2 observed.foo = 2
expect(observed.foo).toBe(1) expect(observed.foo).toBe(1)
expect(warn).toHaveBeenCalledTimes(1) expect(warn).toHaveBeenCalledTimes(1)
@ -76,7 +76,7 @@ describe('reactivity/immutable', () => {
}) })
it('should not trigger effects when locked', () => { it('should not trigger effects when locked', () => {
const observed = immutable({ a: 1 }) const observed: any = immutable({ a: 1 })
let dummy let dummy
effect(() => { effect(() => {
dummy = observed.a dummy = observed.a
@ -88,7 +88,7 @@ describe('reactivity/immutable', () => {
}) })
it('should trigger effects when unlocked', () => { it('should trigger effects when unlocked', () => {
const observed = immutable({ a: 1 }) const observed: any = immutable({ a: 1 })
let dummy let dummy
effect(() => { effect(() => {
dummy = observed.a dummy = observed.a
@ -146,7 +146,7 @@ describe('reactivity/immutable', () => {
}) })
it('should allow mutation when unlocked', () => { it('should allow mutation when unlocked', () => {
const observed: any[] = immutable([{ foo: 1, bar: { baz: 2 } }]) const observed: any = immutable([{ foo: 1, bar: { baz: 2 } }])
unlock() unlock()
observed[1] = 2 observed[1] = 2
observed.push(3) observed.push(3)
@ -162,7 +162,7 @@ describe('reactivity/immutable', () => {
}) })
it('should not trigger effects when locked', () => { it('should not trigger effects when locked', () => {
const observed = immutable([{ a: 1 }]) const observed: any = immutable([{ a: 1 }])
let dummy let dummy
effect(() => { effect(() => {
dummy = observed[0].a dummy = observed[0].a
@ -177,7 +177,7 @@ describe('reactivity/immutable', () => {
}) })
it('should trigger effects when unlocked', () => { it('should trigger effects when unlocked', () => {
const observed = immutable([{ a: 1 }]) const observed: any = immutable([{ a: 1 }])
let dummy let dummy
effect(() => { effect(() => {
dummy = observed[0].a dummy = observed[0].a
@ -256,7 +256,7 @@ describe('reactivity/immutable', () => {
const key1 = {} const key1 = {}
const key2 = {} const key2 = {}
const original = new Collection([[key1, {}], [key2, {}]]) const original = new Collection([[key1, {}], [key2, {}]])
const observed = immutable(original) const observed: any = immutable(original)
for (const [key, value] of observed) { for (const [key, value] of observed) {
expect(isImmutable(key)).toBe(true) expect(isImmutable(key)).toBe(true)
expect(isImmutable(value)).toBe(true) expect(isImmutable(value)).toBe(true)
@ -322,7 +322,7 @@ describe('reactivity/immutable', () => {
if (Collection === Set) { if (Collection === Set) {
test('should retrive immutable values on iteration', () => { test('should retrive immutable values on iteration', () => {
const original = new Collection([{}, {}]) const original = new Collection([{}, {}])
const observed = immutable(original) const observed: any = immutable(original)
for (const value of observed) { for (const value of observed) {
expect(isImmutable(value)).toBe(true) expect(isImmutable(value)).toBe(true)
} }

View File

@ -32,7 +32,7 @@ describe('reactivity/value', () => {
expect(dummy).toBe(2) expect(dummy).toBe(2)
}) })
it('should work like a normal property when nested in an observable', () => { it('should work like a normal property when nested in a reactive object', () => {
const a = ref(1) const a = ref(1)
const obj = reactive({ const obj = reactive({
a, a,

View File

@ -1,5 +1,5 @@
import { effect, ReactiveEffect, activeReactiveEffectStack } from './effect' import { effect, ReactiveEffect, activeReactiveEffectStack } from './effect'
import { UnwrapNestedRefs, knownRefs } from './ref' import { UnwrapNestedRefs, knownRefs, Ref } from './ref'
import { isFunction } from '@vue/shared' import { isFunction } from '@vue/shared'
export interface ComputedRef<T> { export interface ComputedRef<T> {
@ -12,9 +12,11 @@ export interface ComputedOptions<T> {
set: (v: T) => void set: (v: T) => void
} }
export function computed<T>(getter: () => T): ComputedRef<T>
export function computed<T>(options: ComputedOptions<T>): Ref<T>
export function computed<T>( export function computed<T>(
getterOrOptions: (() => T) | ComputedOptions<T> getterOrOptions: (() => T) | ComputedOptions<T>
): ComputedRef<T> { ): Ref<T> {
const isReadonly = isFunction(getterOrOptions) const isReadonly = isFunction(getterOrOptions)
const getter = isReadonly const getter = isReadonly
? (getterOrOptions as (() => T)) ? (getterOrOptions as (() => T))

View File

@ -6,7 +6,7 @@ import {
immutableCollectionHandlers immutableCollectionHandlers
} from './collectionHandlers' } from './collectionHandlers'
import { UnwrapRef } from './ref' import { UnwrapNestedRefs } from './ref'
import { ReactiveEffect } from './effect' import { ReactiveEffect } from './effect'
// The main WeakMap that stores {target -> key -> dep} connections. // The main WeakMap that stores {target -> key -> dep} connections.
@ -40,9 +40,8 @@ const canObserve = (value: any): boolean => {
) )
} }
type ObservableFactory = <T>(target?: T) => UnwrapRef<T> export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
export const reactive = ((target: unknown): any => {
// if trying to observe an immutable proxy, return the immutable version. // if trying to observe an immutable proxy, return the immutable version.
if (immutableToRaw.has(target)) { if (immutableToRaw.has(target)) {
return target return target
@ -58,9 +57,12 @@ export const reactive = ((target: unknown): any => {
mutableHandlers, mutableHandlers,
mutableCollectionHandlers mutableCollectionHandlers
) )
}) as ObservableFactory }
export const immutable = ((target: unknown): any => { export function immutable<T extends object>(
target: T
): Readonly<UnwrapNestedRefs<T>>
export function immutable(target: object) {
// value is a mutable observable, retrive its original and return // value is a mutable observable, retrive its original and return
// a readonly version. // a readonly version.
if (observedToRaw.has(target)) { if (observedToRaw.has(target)) {
@ -73,7 +75,7 @@ export const immutable = ((target: unknown): any => {
immutableHandlers, immutableHandlers,
immutableCollectionHandlers immutableCollectionHandlers
) )
}) as ObservableFactory }
function createReactiveObject( function createReactiveObject(
target: any, target: any,

View File

@ -21,6 +21,7 @@ export {
} from '@vue/reactivity' } from '@vue/reactivity'
import { import {
Ref,
computed as _computed, computed as _computed,
ComputedRef, ComputedRef,
ComputedOptions, ComputedOptions,
@ -37,9 +38,9 @@ export function recordEffect(effect: ReactiveEffect) {
} }
} }
export function computed<T>( export function computed<T>(getter: () => T): ComputedRef<T>
getterOrOptions: (() => T) | ComputedOptions<T> export function computed<T>(options: ComputedOptions<T>): Ref<T>
): ComputedRef<T> { export function computed<T>(getterOrOptions: any) {
const c = _computed(getterOrOptions) const c = _computed(getterOrOptions)
recordEffect(c.effect) recordEffect(c.effect)
return c return c