Просмотр исходного кода

wip: improve computed typing + test for setters

Evan You 6 лет назад
Родитель
Сommit
0aff144f93

+ 34 - 2
packages/reactivity/__tests__/computed.spec.ts

@@ -1,4 +1,4 @@
-import { computed, reactive, effect, stop } from '../src'
+import { computed, reactive, effect, stop, ref } from '../src'
 
 describe('reactivity/computed', () => {
   it('should return updated value', () => {
@@ -123,6 +123,38 @@ describe('reactivity/computed', () => {
   })
 
   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)
   })
 })

+ 8 - 8
packages/reactivity/__tests__/immutable.spec.ts

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

+ 1 - 1
packages/reactivity/__tests__/ref.spec.ts

@@ -32,7 +32,7 @@ describe('reactivity/value', () => {
     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 obj = reactive({
       a,

+ 4 - 2
packages/reactivity/src/computed.ts

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

+ 9 - 7
packages/reactivity/src/reactive.ts

@@ -6,7 +6,7 @@ import {
   immutableCollectionHandlers
 } from './collectionHandlers'
 
-import { UnwrapRef } from './ref'
+import { UnwrapNestedRefs } from './ref'
 import { ReactiveEffect } from './effect'
 
 // 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 const reactive = ((target: unknown): any => {
+export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
+export function reactive(target: object) {
   // if trying to observe an immutable proxy, return the immutable version.
   if (immutableToRaw.has(target)) {
     return target
@@ -58,9 +57,12 @@ export const reactive = ((target: unknown): any => {
     mutableHandlers,
     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
   // a readonly version.
   if (observedToRaw.has(target)) {
@@ -73,7 +75,7 @@ export const immutable = ((target: unknown): any => {
     immutableHandlers,
     immutableCollectionHandlers
   )
-}) as ObservableFactory
+}
 
 function createReactiveObject(
   target: any,

+ 4 - 3
packages/runtime-core/src/apiReactivity.ts

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