]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: test + typing for value
authorEvan You <yyx990803@gmail.com>
Wed, 29 May 2019 14:11:33 +0000 (22:11 +0800)
committerEvan You <yyx990803@gmail.com>
Wed, 29 May 2019 14:11:33 +0000 (22:11 +0800)
packages/observer/__tests__/value.spec.ts
packages/observer/src/index.ts
packages/observer/src/value.ts
packages/runtime-core/src/component.ts

index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fc49e8c3bdbdcceca5bad9d43449339ee3275f3e 100644 (file)
@@ -0,0 +1,54 @@
+import { value } from '../src/value'
+import { effect, observable } from '../src/index'
+
+describe('observer/value', () => {
+  it('should hold a value', () => {
+    const a = value(1)
+    expect(a.value).toBe(1)
+    a.value = 2
+    expect(a.value).toBe(2)
+  })
+
+  it('should be reactive', () => {
+    const a = value(1)
+    let dummy
+    effect(() => {
+      dummy = a.value
+    })
+    expect(dummy).toBe(1)
+    a.value = 2
+    expect(dummy).toBe(2)
+  })
+
+  it('should make nested properties reactive', () => {
+    const a = value({
+      count: 1
+    })
+    let dummy
+    effect(() => {
+      dummy = a.value.count
+    })
+    expect(dummy).toBe(1)
+    a.value.count = 2
+    expect(dummy).toBe(2)
+  })
+
+  it('should work like a normal property when nested in an observable', () => {
+    const a = value(1)
+    const obj = observable({
+      a,
+      b: {
+        c: a
+      }
+    })
+    let dummy
+    effect(() => {
+      dummy = obj.a
+    })
+    expect(dummy).toBe(1)
+    a.value++
+    expect(dummy).toBe(2)
+    obj.a++
+    expect(dummy).toBe(3)
+  })
+})
index 423126fc1caa460769c6d2b65021cd0760568b79..f3aea4e6d001368e3a97064cc6e0a16ed88f3522 100644 (file)
@@ -24,11 +24,13 @@ import {
   DebuggerEvent
 } from './effect'
 
+import { UnwrapBindings } from './value'
+
 export { ReactiveEffect, ReactiveEffectOptions, DebuggerEvent }
 export { OperationTypes } from './operations'
 export { computed, ComputedValue } from './computed'
 export { lock, unlock } from './lock'
-export { value, isValue, Value } from './value'
+export { value, isValue, Value, UnwrapBindings } from './value'
 
 const collectionTypes: Set<any> = new Set([Set, Map, WeakMap, WeakSet])
 const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/
@@ -42,7 +44,7 @@ const canObserve = (value: any): boolean => {
   )
 }
 
-type identity = <T>(target?: T) => T
+type ObservableFactory = <T>(target?: T) => UnwrapBindings<T>
 
 export const observable = ((target: any = {}): any => {
   // if trying to observe an immutable proxy, return the immutable version.
@@ -60,7 +62,7 @@ export const observable = ((target: any = {}): any => {
     mutableHandlers,
     mutableCollectionHandlers
   )
-}) as identity
+}) as ObservableFactory
 
 export const immutable = ((target: any = {}): any => {
   // value is a mutable observable, retrive its original and return
@@ -75,7 +77,7 @@ export const immutable = ((target: any = {}): any => {
     immutableHandlers,
     immutableCollectionHandlers
   )
-}) as identity
+}) as ObservableFactory
 
 function createObservable(
   target: any,
index 534ee352ad57e8e4b69b3523cc0ff17856428466..c274eb0262b5d43b956e5fe78875ab2b03bfff5d 100644 (file)
@@ -9,6 +9,62 @@ export interface Value<T> {
   value: T
 }
 
+type UnwrapValue<T, U = T> = T extends Value<infer V> ? V : T extends {} ? U : T
+
+// A utility type that recursively unwraps value bindings nested inside an
+// observable object. Unfortunately TS cannot do recursive types, but this
+// should be enough for practical use cases...
+export type UnwrapBindings<T> = {
+  [key in keyof T]: UnwrapValue<
+    T[key],
+    {
+      [k2 in keyof T[key]]: UnwrapValue<
+        T[key][k2],
+        {
+          [k3 in keyof T[key][k2]]: UnwrapValue<
+            T[key][k2][k3],
+            {
+              [k4 in keyof T[key][k2][k3]]: UnwrapValue<
+                T[key][k2][k3][k4],
+                {
+                  [k5 in keyof T[key][k2][k3][k4]]: UnwrapValue<
+                    T[key][k2][k3][k4][k5],
+                    {
+                      [k6 in keyof T[key][k2][k3][k4][k5]]: UnwrapValue<
+                        T[key][k2][k3][k4][k5][k6],
+                        {
+                          [k7 in keyof T[key][k2][k3][k4][k5][k6]]: UnwrapValue<
+                            T[key][k2][k3][k4][k5][k6][k7],
+                            {
+                              [k8 in keyof T[key][k2][k3][k4][k5][k6][k7]]: UnwrapValue<
+                                T[key][k2][k3][k4][k5][k6][k7][k8],
+                                {
+                                  [k9 in keyof T[key][k2][k3][k4][k5][k6][k7][k8]]: UnwrapValue<
+                                    T[key][k2][k3][k4][k5][k6][k7][k8][k9],
+                                    {
+                                      [k10 in keyof T[key][k2][k3][k4][k5][k6][k7][k8][k9]]: UnwrapValue<
+                                        T[key][k2][k3][k4][k5][k6][k7][k8][k9][k10]
+                                      >
+                                    }
+                                  >
+                                }
+                              >
+                            }
+                          >
+                        }
+                      >
+                    }
+                  >
+                }
+              >
+            }
+          >
+        }
+      >
+    }
+  >
+}
+
 const convert = (val: any): any => (isObject(val) ? observable(val) : val)
 
 export function value<T>(raw: T): Value<T> {
index a1728939266e12880493c781e568d94403d9a03d..99416c562447f311f15e2e2ea33f56a9b5950850 100644 (file)
@@ -1,19 +1,11 @@
 import { VNode, normalizeVNode, VNodeChild } from './vnode'
-import { ReactiveEffect, observable } from '@vue/observer'
+import { ReactiveEffect, UnwrapBindings, observable } from '@vue/observer'
 import { isFunction, EMPTY_OBJ } from '@vue/shared'
 import { RenderProxyHandlers } from './componentProxy'
 import { ComponentPropsOptions, PropValidator } from './componentProps'
 
-interface Value<T> {
-  value: T
-}
-
 export type Data = { [key: string]: any }
 
-type UnwrapBindings<T> = {
-  [key in keyof T]: T[key] extends Value<infer V> ? V : T[key]
-}
-
 type ExtractPropTypes<PropOptions> = {
   readonly [key in keyof PropOptions]: PropOptions[key] extends PropValidator<
     infer V