types: improve value unwrapping

This commit is contained in:
Evan You 2019-05-30 13:35:50 +08:00
parent 6441db45c7
commit b82b7af29b
5 changed files with 114 additions and 72 deletions

View File

@ -38,17 +38,28 @@ describe('observer/value', () => {
const obj = observable({ const obj = observable({
a, a,
b: { b: {
c: a c: a,
d: [a]
} }
}) })
let dummy let dummy1
let dummy2
let dummy3
effect(() => { effect(() => {
dummy = obj.a dummy1 = obj.a
dummy2 = obj.b.c
dummy3 = obj.b.d[0]
}) })
expect(dummy).toBe(1) expect(dummy1).toBe(1)
expect(dummy2).toBe(1)
expect(dummy3).toBe(1)
a.value++ a.value++
expect(dummy).toBe(2) expect(dummy1).toBe(2)
expect(dummy2).toBe(2)
expect(dummy3).toBe(2)
obj.a++ obj.a++
expect(dummy).toBe(3) expect(dummy1).toBe(3)
expect(dummy2).toBe(3)
expect(dummy3).toBe(3)
}) })
}) })

View File

@ -73,7 +73,7 @@ export function cleanup(effect: ReactiveEffect) {
for (let i = 0; i < effect.deps.length; i++) { for (let i = 0; i < effect.deps.length; i++) {
effect.deps[i].delete(effect) effect.deps[i].delete(effect)
} }
effect.deps = [] effect.deps.length = 0
} }
export function track( export function track(

View File

@ -24,13 +24,13 @@ import {
DebuggerEvent DebuggerEvent
} from './effect' } from './effect'
import { UnwrapValues } from './value' import { UnwrapValue } 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, UnwrapValues } from './value' export { value, isValue, Value, UnwrapValue } 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)\]$/
@ -44,7 +44,7 @@ const canObserve = (value: any): boolean => {
) )
} }
type ObservableFactory = <T>(target?: T) => UnwrapValues<T> type ObservableFactory = <T>(target?: T) => UnwrapValue<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.

View File

@ -31,58 +31,88 @@ export function isValue(v: any): v is Value<any> {
return knownValues.has(v) return knownValues.has(v)
} }
type UnwrapValue<T, U = T> = T extends Value<infer V> ? V : T extends {} ? U : T type BailTypes =
| Function
| Map<any, any>
| Set<any>
| WeakMap<any, any>
| WeakSet<any>
// A utility type that recursively unwraps value bindings nested inside an // Recursively unwraps nested value bindings.
// observable object. Unfortunately TS cannot do recursive types, but this // Unfortunately TS cannot do recursive types, but this should be enough for
// should be enough for practical use cases... // practical use cases...
export type UnwrapValues<T> = { export type UnwrapValue<T> = T extends Value<infer V>
[key in keyof T]: UnwrapValue< ? UnwrapValue2<V>
T[key], : T extends Array<infer V>
{ ? Array<UnwrapValue2<V>>
[k2 in keyof T[key]]: UnwrapValue< : T extends BailTypes
T[key][k2], ? T // bail out on types that shouldn't be unwrapped
{ : T extends object ? { [K in keyof T]: UnwrapValue2<T[K]> } : T
[k3 in keyof T[key][k2]]: UnwrapValue<
T[key][k2][k3], type UnwrapValue2<T> = T extends Value<infer V>
{ ? UnwrapValue3<V>
[k4 in keyof T[key][k2][k3]]: UnwrapValue< : T extends Array<infer V>
T[key][k2][k3][k4], ? Array<UnwrapValue3<V>>
{ : T extends BailTypes
[k5 in keyof T[key][k2][k3][k4]]: UnwrapValue< ? T
T[key][k2][k3][k4][k5], : T extends object ? { [K in keyof T]: UnwrapValue3<T[K]> } : T
{
[k6 in keyof T[key][k2][k3][k4][k5]]: UnwrapValue< type UnwrapValue3<T> = T extends Value<infer V>
T[key][k2][k3][k4][k5][k6], ? UnwrapValue4<V>
{ : T extends Array<infer V>
[k7 in keyof T[key][k2][k3][k4][k5][k6]]: UnwrapValue< ? Array<UnwrapValue4<V>>
T[key][k2][k3][k4][k5][k6][k7], : T extends BailTypes
{ ? T
[k8 in keyof T[key][k2][k3][k4][k5][k6][k7]]: UnwrapValue< : T extends object ? { [K in keyof T]: UnwrapValue4<T[K]> } : T
T[key][k2][k3][k4][k5][k6][k7][k8],
{ type UnwrapValue4<T> = T extends Value<infer V>
[k9 in keyof T[key][k2][k3][k4][k5][k6][k7][k8]]: UnwrapValue< ? UnwrapValue5<V>
T[key][k2][k3][k4][k5][k6][k7][k8][k9], : T extends Array<infer V>
{ ? Array<UnwrapValue5<V>>
[k10 in keyof T[key][k2][k3][k4][k5][k6][k7][k8][k9]]: UnwrapValue< : T extends BailTypes
T[key][k2][k3][k4][k5][k6][k7][k8][k9][k10] ? T
> : T extends object ? { [K in keyof T]: UnwrapValue5<T[K]> } : T
}
> type UnwrapValue5<T> = T extends Value<infer V>
} ? UnwrapValue6<V>
> : T extends Array<infer V>
} ? Array<UnwrapValue6<V>>
> : T extends BailTypes
} ? T
> : T extends object ? { [K in keyof T]: UnwrapValue6<T[K]> } : T
}
> type UnwrapValue6<T> = T extends Value<infer V>
} ? UnwrapValue7<V>
> : T extends Array<infer V>
} ? Array<UnwrapValue7<V>>
> : T extends BailTypes
} ? T
> : T extends object ? { [K in keyof T]: UnwrapValue7<T[K]> } : T
}
> type UnwrapValue7<T> = T extends Value<infer V>
} ? UnwrapValue8<V>
: T extends Array<infer V>
? Array<UnwrapValue8<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapValue8<T[K]> } : T
type UnwrapValue8<T> = T extends Value<infer V>
? UnwrapValue9<V>
: T extends Array<infer V>
? Array<UnwrapValue9<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapValue9<T[K]> } : T
type UnwrapValue9<T> = T extends Value<infer V>
? UnwrapValue10<V>
: T extends Array<infer V>
? Array<UnwrapValue10<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapValue10<T[K]> } : T
type UnwrapValue10<T> = T extends Value<infer V>
? V // stop recursion
: T

View File

@ -1,5 +1,5 @@
import { VNode, normalizeVNode, VNodeChild } from './vnode' import { VNode, normalizeVNode, VNodeChild } from './vnode'
import { ReactiveEffect, UnwrapValues, observable } from '@vue/observer' import { ReactiveEffect, UnwrapValue, 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'
@ -14,7 +14,7 @@ type ExtractPropTypes<PropOptions> = {
: PropOptions[key] extends null | undefined ? any : PropOptions[key] : PropOptions[key] extends null | undefined ? any : PropOptions[key]
} }
export interface ComponentPublicProperties<P = Data, S = Data> { export type ComponentPublicProperties<P = Data, S = Data> = {
$state: S $state: S
$props: P $props: P
$attrs: Data $attrs: Data
@ -25,19 +25,20 @@ export interface ComponentPublicProperties<P = Data, S = Data> {
$root: ComponentInstance | null $root: ComponentInstance | null
$parent: ComponentInstance | null $parent: ComponentInstance | null
} } & P &
S
export interface ComponentOptions< export interface ComponentOptions<
RawProps = ComponentPropsOptions, RawProps = ComponentPropsOptions,
RawBindings = Data | void, RawBindings = Data | void,
Props = ExtractPropTypes<RawProps>, Props = ExtractPropTypes<RawProps>,
Bindings = UnwrapValues<RawBindings> Bindings = UnwrapValue<RawBindings>
> { > {
props?: RawProps props?: RawProps
setup?: (props: Props) => RawBindings setup?: (props: Props) => RawBindings
render?: <B extends Bindings>( render?: <State extends Bindings>(
this: ComponentPublicProperties<Props, B>, this: ComponentPublicProperties<Props, State>,
ctx: ComponentInstance<Props, B> ctx: ComponentInstance<Props, State>
) => VNodeChild ) => VNodeChild
} }
@ -90,7 +91,7 @@ export function createComponent<
RawProps, RawProps,
RawBindings, RawBindings,
Props = ExtractPropTypes<RawProps>, Props = ExtractPropTypes<RawProps>,
Bindings = UnwrapValues<RawBindings> Bindings = UnwrapValue<RawBindings>
>( >(
options: ComponentOptions<RawProps, RawBindings, Props, Bindings> options: ComponentOptions<RawProps, RawBindings, Props, Bindings>
): { ): {