From b82b7af29bef142b1c9457cc8eef70cde8227d89 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 30 May 2019 13:35:50 +0800 Subject: [PATCH] types: improve value unwrapping --- packages/observer/__tests__/value.spec.ts | 23 +++- packages/observer/src/effect.ts | 2 +- packages/observer/src/index.ts | 6 +- packages/observer/src/value.ts | 138 +++++++++++++--------- packages/runtime-core/src/component.ts | 17 +-- 5 files changed, 114 insertions(+), 72 deletions(-) diff --git a/packages/observer/__tests__/value.spec.ts b/packages/observer/__tests__/value.spec.ts index fc49e8c3b..2e46dfcb6 100644 --- a/packages/observer/__tests__/value.spec.ts +++ b/packages/observer/__tests__/value.spec.ts @@ -38,17 +38,28 @@ describe('observer/value', () => { const obj = observable({ a, b: { - c: a + c: a, + d: [a] } }) - let dummy + let dummy1 + let dummy2 + let dummy3 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++ - expect(dummy).toBe(2) + expect(dummy1).toBe(2) + expect(dummy2).toBe(2) + expect(dummy3).toBe(2) obj.a++ - expect(dummy).toBe(3) + expect(dummy1).toBe(3) + expect(dummy2).toBe(3) + expect(dummy3).toBe(3) }) }) diff --git a/packages/observer/src/effect.ts b/packages/observer/src/effect.ts index 37de5815c..819d7e6a3 100644 --- a/packages/observer/src/effect.ts +++ b/packages/observer/src/effect.ts @@ -73,7 +73,7 @@ export function cleanup(effect: ReactiveEffect) { for (let i = 0; i < effect.deps.length; i++) { effect.deps[i].delete(effect) } - effect.deps = [] + effect.deps.length = 0 } export function track( diff --git a/packages/observer/src/index.ts b/packages/observer/src/index.ts index 44669a798..0e8781ebd 100644 --- a/packages/observer/src/index.ts +++ b/packages/observer/src/index.ts @@ -24,13 +24,13 @@ import { DebuggerEvent } from './effect' -import { UnwrapValues } from './value' +import { UnwrapValue } from './value' export { ReactiveEffect, ReactiveEffectOptions, DebuggerEvent } export { OperationTypes } from './operations' export { computed, ComputedValue } from './computed' export { lock, unlock } from './lock' -export { value, isValue, Value, UnwrapValues } from './value' +export { value, isValue, Value, UnwrapValue } from './value' const collectionTypes: Set = new Set([Set, Map, WeakMap, WeakSet]) const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/ @@ -44,7 +44,7 @@ const canObserve = (value: any): boolean => { ) } -type ObservableFactory = (target?: T) => UnwrapValues +type ObservableFactory = (target?: T) => UnwrapValue export const observable = ((target: any = {}): any => { // if trying to observe an immutable proxy, return the immutable version. diff --git a/packages/observer/src/value.ts b/packages/observer/src/value.ts index 4101bbd35..306aab17c 100644 --- a/packages/observer/src/value.ts +++ b/packages/observer/src/value.ts @@ -31,58 +31,88 @@ export function isValue(v: any): v is Value { return knownValues.has(v) } -type UnwrapValue = T extends Value ? V : T extends {} ? U : T +type BailTypes = + | Function + | Map + | Set + | WeakMap + | WeakSet -// 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 UnwrapValues = { - [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] - > - } - > - } - > - } - > - } - > - } - > - } - > - } - > - } - > - } - > -} +// Recursively unwraps nested value bindings. +// Unfortunately TS cannot do recursive types, but this should be enough for +// practical use cases... +export type UnwrapValue = T extends Value + ? UnwrapValue2 + : T extends Array + ? Array> + : T extends BailTypes + ? T // bail out on types that shouldn't be unwrapped + : T extends object ? { [K in keyof T]: UnwrapValue2 } : T + +type UnwrapValue2 = T extends Value + ? UnwrapValue3 + : T extends Array + ? Array> + : T extends BailTypes + ? T + : T extends object ? { [K in keyof T]: UnwrapValue3 } : T + +type UnwrapValue3 = T extends Value + ? UnwrapValue4 + : T extends Array + ? Array> + : T extends BailTypes + ? T + : T extends object ? { [K in keyof T]: UnwrapValue4 } : T + +type UnwrapValue4 = T extends Value + ? UnwrapValue5 + : T extends Array + ? Array> + : T extends BailTypes + ? T + : T extends object ? { [K in keyof T]: UnwrapValue5 } : T + +type UnwrapValue5 = T extends Value + ? UnwrapValue6 + : T extends Array + ? Array> + : T extends BailTypes + ? T + : T extends object ? { [K in keyof T]: UnwrapValue6 } : T + +type UnwrapValue6 = T extends Value + ? UnwrapValue7 + : T extends Array + ? Array> + : T extends BailTypes + ? T + : T extends object ? { [K in keyof T]: UnwrapValue7 } : T + +type UnwrapValue7 = T extends Value + ? UnwrapValue8 + : T extends Array + ? Array> + : T extends BailTypes + ? T + : T extends object ? { [K in keyof T]: UnwrapValue8 } : T + +type UnwrapValue8 = T extends Value + ? UnwrapValue9 + : T extends Array + ? Array> + : T extends BailTypes + ? T + : T extends object ? { [K in keyof T]: UnwrapValue9 } : T + +type UnwrapValue9 = T extends Value + ? UnwrapValue10 + : T extends Array + ? Array> + : T extends BailTypes + ? T + : T extends object ? { [K in keyof T]: UnwrapValue10 } : T + +type UnwrapValue10 = T extends Value + ? V // stop recursion + : T diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index e2db5c110..8b7cea0bc 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -1,5 +1,5 @@ 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 { RenderProxyHandlers } from './componentProxy' import { ComponentPropsOptions, PropValidator } from './componentProps' @@ -14,7 +14,7 @@ type ExtractPropTypes = { : PropOptions[key] extends null | undefined ? any : PropOptions[key] } -export interface ComponentPublicProperties

{ +export type ComponentPublicProperties

= { $state: S $props: P $attrs: Data @@ -25,19 +25,20 @@ export interface ComponentPublicProperties

{ $root: ComponentInstance | null $parent: ComponentInstance | null -} +} & P & + S export interface ComponentOptions< RawProps = ComponentPropsOptions, RawBindings = Data | void, Props = ExtractPropTypes, - Bindings = UnwrapValues + Bindings = UnwrapValue > { props?: RawProps setup?: (props: Props) => RawBindings - render?: ( - this: ComponentPublicProperties, - ctx: ComponentInstance + render?: ( + this: ComponentPublicProperties, + ctx: ComponentInstance ) => VNodeChild } @@ -90,7 +91,7 @@ export function createComponent< RawProps, RawBindings, Props = ExtractPropTypes, - Bindings = UnwrapValues + Bindings = UnwrapValue >( options: ComponentOptions ): {