wip: test + typing for value
This commit is contained in:
parent
ef4fde3522
commit
dde6c151e4
@ -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)
|
||||||
|
})
|
||||||
|
})
|
@ -24,11 +24,13 @@ import {
|
|||||||
DebuggerEvent
|
DebuggerEvent
|
||||||
} from './effect'
|
} from './effect'
|
||||||
|
|
||||||
|
import { UnwrapBindings } from './value'
|
||||||
|
|
||||||
export { ReactiveEffect, ReactiveEffectOptions, DebuggerEvent }
|
export { ReactiveEffect, ReactiveEffectOptions, DebuggerEvent }
|
||||||
export { OperationTypes } from './operations'
|
export { OperationTypes } from './operations'
|
||||||
export { computed, ComputedValue } from './computed'
|
export { computed, ComputedValue } from './computed'
|
||||||
export { lock, unlock } from './lock'
|
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 collectionTypes: Set<any> = new Set([Set, Map, WeakMap, WeakSet])
|
||||||
const observableValueRE = /^\[object (?:Object|Array|Map|Set|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 => {
|
export const observable = ((target: any = {}): any => {
|
||||||
// if trying to observe an immutable proxy, return the immutable version.
|
// if trying to observe an immutable proxy, return the immutable version.
|
||||||
@ -60,7 +62,7 @@ export const observable = ((target: any = {}): any => {
|
|||||||
mutableHandlers,
|
mutableHandlers,
|
||||||
mutableCollectionHandlers
|
mutableCollectionHandlers
|
||||||
)
|
)
|
||||||
}) as identity
|
}) as ObservableFactory
|
||||||
|
|
||||||
export const immutable = ((target: any = {}): any => {
|
export const immutable = ((target: any = {}): any => {
|
||||||
// value is a mutable observable, retrive its original and return
|
// value is a mutable observable, retrive its original and return
|
||||||
@ -75,7 +77,7 @@ export const immutable = ((target: any = {}): any => {
|
|||||||
immutableHandlers,
|
immutableHandlers,
|
||||||
immutableCollectionHandlers
|
immutableCollectionHandlers
|
||||||
)
|
)
|
||||||
}) as identity
|
}) as ObservableFactory
|
||||||
|
|
||||||
function createObservable(
|
function createObservable(
|
||||||
target: any,
|
target: any,
|
||||||
|
@ -9,6 +9,62 @@ export interface Value<T> {
|
|||||||
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)
|
const convert = (val: any): any => (isObject(val) ? observable(val) : val)
|
||||||
|
|
||||||
export function value<T>(raw: T): Value<T> {
|
export function value<T>(raw: T): Value<T> {
|
||||||
|
@ -1,19 +1,11 @@
|
|||||||
import { VNode, normalizeVNode, VNodeChild } from './vnode'
|
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 { isFunction, EMPTY_OBJ } from '@vue/shared'
|
||||||
import { RenderProxyHandlers } from './componentProxy'
|
import { RenderProxyHandlers } from './componentProxy'
|
||||||
import { ComponentPropsOptions, PropValidator } from './componentProps'
|
import { ComponentPropsOptions, PropValidator } from './componentProps'
|
||||||
|
|
||||||
interface Value<T> {
|
|
||||||
value: T
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Data = { [key: string]: any }
|
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> = {
|
type ExtractPropTypes<PropOptions> = {
|
||||||
readonly [key in keyof PropOptions]: PropOptions[key] extends PropValidator<
|
readonly [key in keyof PropOptions]: PropOptions[key] extends PropValidator<
|
||||||
infer V
|
infer V
|
||||||
|
Loading…
Reference in New Issue
Block a user