types: massive refactor
This commit is contained in:
@@ -12,7 +12,7 @@ const builtInSymbols = new Set(
|
||||
)
|
||||
|
||||
function createGetter(isReadonly: boolean) {
|
||||
return function get(target: any, key: string | symbol, receiver: any) {
|
||||
return function get(target: object, key: string | symbol, receiver: object) {
|
||||
const res = Reflect.get(target, key, receiver)
|
||||
if (isSymbol(key) && builtInSymbols.has(key)) {
|
||||
return res
|
||||
@@ -32,13 +32,13 @@ function createGetter(isReadonly: boolean) {
|
||||
}
|
||||
|
||||
function set(
|
||||
target: any,
|
||||
target: object,
|
||||
key: string | symbol,
|
||||
value: any,
|
||||
receiver: any
|
||||
value: unknown,
|
||||
receiver: object
|
||||
): boolean {
|
||||
value = toRaw(value)
|
||||
const oldValue = target[key]
|
||||
const oldValue = (target as any)[key]
|
||||
if (isRef(oldValue) && !isRef(value)) {
|
||||
oldValue.value = value
|
||||
return true
|
||||
@@ -66,9 +66,9 @@ function set(
|
||||
return result
|
||||
}
|
||||
|
||||
function deleteProperty(target: any, key: string | symbol): boolean {
|
||||
function deleteProperty(target: object, key: string | symbol): boolean {
|
||||
const hadKey = hasOwn(target, key)
|
||||
const oldValue = target[key]
|
||||
const oldValue = (target as any)[key]
|
||||
const result = Reflect.deleteProperty(target, key)
|
||||
if (result && hadKey) {
|
||||
/* istanbul ignore else */
|
||||
@@ -81,18 +81,18 @@ function deleteProperty(target: any, key: string | symbol): boolean {
|
||||
return result
|
||||
}
|
||||
|
||||
function has(target: any, key: string | symbol): boolean {
|
||||
function has(target: object, key: string | symbol): boolean {
|
||||
const result = Reflect.has(target, key)
|
||||
track(target, OperationTypes.HAS, key)
|
||||
return result
|
||||
}
|
||||
|
||||
function ownKeys(target: any): (string | number | symbol)[] {
|
||||
function ownKeys(target: object): (string | number | symbol)[] {
|
||||
track(target, OperationTypes.ITERATE)
|
||||
return Reflect.ownKeys(target)
|
||||
}
|
||||
|
||||
export const mutableHandlers: ProxyHandler<any> = {
|
||||
export const mutableHandlers: ProxyHandler<object> = {
|
||||
get: createGetter(false),
|
||||
set,
|
||||
deleteProperty,
|
||||
@@ -100,10 +100,15 @@ export const mutableHandlers: ProxyHandler<any> = {
|
||||
ownKeys
|
||||
}
|
||||
|
||||
export const readonlyHandlers: ProxyHandler<any> = {
|
||||
export const readonlyHandlers: ProxyHandler<object> = {
|
||||
get: createGetter(true),
|
||||
|
||||
set(target: any, key: string | symbol, value: any, receiver: any): boolean {
|
||||
set(
|
||||
target: object,
|
||||
key: string | symbol,
|
||||
value: unknown,
|
||||
receiver: object
|
||||
): boolean {
|
||||
if (LOCKED) {
|
||||
if (__DEV__) {
|
||||
console.warn(
|
||||
@@ -117,7 +122,7 @@ export const readonlyHandlers: ProxyHandler<any> = {
|
||||
}
|
||||
},
|
||||
|
||||
deleteProperty(target: any, key: string | symbol): boolean {
|
||||
deleteProperty(target: object, key: string | symbol): boolean {
|
||||
if (LOCKED) {
|
||||
if (__DEV__) {
|
||||
console.warn(
|
||||
|
||||
@@ -4,43 +4,56 @@ import { OperationTypes } from './operations'
|
||||
import { LOCKED } from './lock'
|
||||
import { isObject, capitalize, hasOwn } from '@vue/shared'
|
||||
|
||||
const toReactive = (value: any) => (isObject(value) ? reactive(value) : value)
|
||||
const toReadonly = (value: any) => (isObject(value) ? readonly(value) : value)
|
||||
export type CollectionTypes = IterableCollections | WeakCollections
|
||||
|
||||
function get(target: any, key: any, wrap: (t: any) => any): any {
|
||||
type IterableCollections = Map<any, any> | Set<any>
|
||||
type WeakCollections = WeakMap<any, any> | WeakSet<any>
|
||||
type MapTypes = Map<any, any> | WeakMap<any, any>
|
||||
type SetTypes = Set<any> | WeakSet<any>
|
||||
|
||||
const toReactive = <T extends unknown>(value: T): T =>
|
||||
isObject(value) ? reactive(value) : value
|
||||
|
||||
const toReadonly = <T extends unknown>(value: T): T =>
|
||||
isObject(value) ? readonly(value) : value
|
||||
|
||||
const getProto = <T extends CollectionTypes>(v: T): any =>
|
||||
Reflect.getPrototypeOf(v)
|
||||
|
||||
function get(
|
||||
target: MapTypes,
|
||||
key: unknown,
|
||||
wrap: typeof toReactive | typeof toReadonly
|
||||
) {
|
||||
target = toRaw(target)
|
||||
key = toRaw(key)
|
||||
const proto: any = Reflect.getPrototypeOf(target)
|
||||
track(target, OperationTypes.GET, key)
|
||||
const res = proto.get.call(target, key)
|
||||
return wrap(res)
|
||||
return wrap(getProto(target).get.call(target, key))
|
||||
}
|
||||
|
||||
function has(this: any, key: any): boolean {
|
||||
function has(this: CollectionTypes, key: unknown): boolean {
|
||||
const target = toRaw(this)
|
||||
key = toRaw(key)
|
||||
const proto: any = Reflect.getPrototypeOf(target)
|
||||
track(target, OperationTypes.HAS, key)
|
||||
return proto.has.call(target, key)
|
||||
return getProto(target).has.call(target, key)
|
||||
}
|
||||
|
||||
function size(target: any) {
|
||||
function size(target: IterableCollections) {
|
||||
target = toRaw(target)
|
||||
const proto = Reflect.getPrototypeOf(target)
|
||||
track(target, OperationTypes.ITERATE)
|
||||
return Reflect.get(proto, 'size', target)
|
||||
return Reflect.get(getProto(target), 'size', target)
|
||||
}
|
||||
|
||||
function add(this: any, value: any) {
|
||||
function add(this: SetTypes, value: unknown) {
|
||||
value = toRaw(value)
|
||||
const target = toRaw(this)
|
||||
const proto: any = Reflect.getPrototypeOf(this)
|
||||
const proto = getProto(target)
|
||||
const hadKey = proto.has.call(target, value)
|
||||
const result = proto.add.call(target, value)
|
||||
if (!hadKey) {
|
||||
/* istanbul ignore else */
|
||||
if (__DEV__) {
|
||||
trigger(target, OperationTypes.ADD, value, { value })
|
||||
trigger(target, OperationTypes.ADD, value, { newValue: value })
|
||||
} else {
|
||||
trigger(target, OperationTypes.ADD, value)
|
||||
}
|
||||
@@ -48,10 +61,10 @@ function add(this: any, value: any) {
|
||||
return result
|
||||
}
|
||||
|
||||
function set(this: any, key: any, value: any) {
|
||||
function set(this: MapTypes, key: unknown, value: unknown) {
|
||||
value = toRaw(value)
|
||||
const target = toRaw(this)
|
||||
const proto: any = Reflect.getPrototypeOf(this)
|
||||
const proto = getProto(target)
|
||||
const hadKey = proto.has.call(target, key)
|
||||
const oldValue = proto.get.call(target, key)
|
||||
const result = proto.set.call(target, key, value)
|
||||
@@ -75,9 +88,9 @@ function set(this: any, key: any, value: any) {
|
||||
return result
|
||||
}
|
||||
|
||||
function deleteEntry(this: any, key: any) {
|
||||
function deleteEntry(this: CollectionTypes, key: unknown) {
|
||||
const target = toRaw(this)
|
||||
const proto: any = Reflect.getPrototypeOf(this)
|
||||
const proto = getProto(target)
|
||||
const hadKey = proto.has.call(target, key)
|
||||
const oldValue = proto.get ? proto.get.call(target, key) : undefined
|
||||
// forward the operation before queueing reactions
|
||||
@@ -93,13 +106,16 @@ function deleteEntry(this: any, key: any) {
|
||||
return result
|
||||
}
|
||||
|
||||
function clear(this: any) {
|
||||
function clear(this: IterableCollections) {
|
||||
const target = toRaw(this)
|
||||
const proto: any = Reflect.getPrototypeOf(this)
|
||||
const hadItems = target.size !== 0
|
||||
const oldTarget = target instanceof Map ? new Map(target) : new Set(target)
|
||||
const oldTarget = __DEV__
|
||||
? target instanceof Map
|
||||
? new Map(target)
|
||||
: new Set(target)
|
||||
: undefined
|
||||
// forward the operation before queueing reactions
|
||||
const result = proto.clear.call(target)
|
||||
const result = getProto(target).clear.call(target)
|
||||
if (hadItems) {
|
||||
/* istanbul ignore else */
|
||||
if (__DEV__) {
|
||||
@@ -112,30 +128,32 @@ function clear(this: any) {
|
||||
}
|
||||
|
||||
function createForEach(isReadonly: boolean) {
|
||||
return function forEach(this: any, callback: Function, thisArg?: any) {
|
||||
return function forEach(
|
||||
this: IterableCollections,
|
||||
callback: Function,
|
||||
thisArg?: unknown
|
||||
) {
|
||||
const observed = this
|
||||
const target = toRaw(observed)
|
||||
const proto: any = Reflect.getPrototypeOf(target)
|
||||
const wrap = isReadonly ? toReadonly : toReactive
|
||||
track(target, OperationTypes.ITERATE)
|
||||
// important: create sure the callback is
|
||||
// 1. invoked with the reactive map as `this` and 3rd arg
|
||||
// 2. the value received should be a corresponding reactive/readonly.
|
||||
function wrappedCallback(value: any, key: any) {
|
||||
function wrappedCallback(value: unknown, key: unknown) {
|
||||
return callback.call(observed, wrap(value), wrap(key), observed)
|
||||
}
|
||||
return proto.forEach.call(target, wrappedCallback, thisArg)
|
||||
return getProto(target).forEach.call(target, wrappedCallback, thisArg)
|
||||
}
|
||||
}
|
||||
|
||||
function createIterableMethod(method: string | symbol, isReadonly: boolean) {
|
||||
return function(this: any, ...args: any[]) {
|
||||
return function(this: IterableCollections, ...args: unknown[]) {
|
||||
const target = toRaw(this)
|
||||
const proto: any = Reflect.getPrototypeOf(target)
|
||||
const isPair =
|
||||
method === 'entries' ||
|
||||
(method === Symbol.iterator && target instanceof Map)
|
||||
const innerIterator = proto[method].apply(target, args)
|
||||
const innerIterator = getProto(target)[method].apply(target, args)
|
||||
const wrap = isReadonly ? toReadonly : toReactive
|
||||
track(target, OperationTypes.ITERATE)
|
||||
// return a wrapped iterator which returns observed versions of the
|
||||
@@ -163,7 +181,7 @@ function createReadonlyMethod(
|
||||
method: Function,
|
||||
type: OperationTypes
|
||||
): Function {
|
||||
return function(this: any, ...args: any[]) {
|
||||
return function(this: CollectionTypes, ...args: unknown[]) {
|
||||
if (LOCKED) {
|
||||
if (__DEV__) {
|
||||
const key = args[0] ? `on key "${args[0]}" ` : ``
|
||||
@@ -179,11 +197,11 @@ function createReadonlyMethod(
|
||||
}
|
||||
}
|
||||
|
||||
const mutableInstrumentations: any = {
|
||||
get(key: any) {
|
||||
const mutableInstrumentations: Record<string, Function> = {
|
||||
get(this: MapTypes, key: unknown) {
|
||||
return get(this, key, toReactive)
|
||||
},
|
||||
get size() {
|
||||
get size(this: IterableCollections) {
|
||||
return size(this)
|
||||
},
|
||||
has,
|
||||
@@ -194,11 +212,11 @@ const mutableInstrumentations: any = {
|
||||
forEach: createForEach(false)
|
||||
}
|
||||
|
||||
const readonlyInstrumentations: any = {
|
||||
get(key: any) {
|
||||
const readonlyInstrumentations: Record<string, Function> = {
|
||||
get(this: MapTypes, key: unknown) {
|
||||
return get(this, key, toReadonly)
|
||||
},
|
||||
get size() {
|
||||
get size(this: IterableCollections) {
|
||||
return size(this)
|
||||
},
|
||||
has,
|
||||
@@ -211,26 +229,37 @@ const readonlyInstrumentations: any = {
|
||||
|
||||
const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]
|
||||
iteratorMethods.forEach(method => {
|
||||
mutableInstrumentations[method] = createIterableMethod(method, false)
|
||||
readonlyInstrumentations[method] = createIterableMethod(method, true)
|
||||
mutableInstrumentations[method as string] = createIterableMethod(
|
||||
method,
|
||||
false
|
||||
)
|
||||
readonlyInstrumentations[method as string] = createIterableMethod(
|
||||
method,
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
function createInstrumentationGetter(instrumentations: any) {
|
||||
return function getInstrumented(
|
||||
target: any,
|
||||
function createInstrumentationGetter(
|
||||
instrumentations: Record<string, Function>
|
||||
) {
|
||||
return (
|
||||
target: CollectionTypes,
|
||||
key: string | symbol,
|
||||
receiver: any
|
||||
) {
|
||||
target =
|
||||
hasOwn(instrumentations, key) && key in target ? instrumentations : target
|
||||
return Reflect.get(target, key, receiver)
|
||||
}
|
||||
receiver: CollectionTypes
|
||||
) =>
|
||||
Reflect.get(
|
||||
hasOwn(instrumentations, key) && key in target
|
||||
? instrumentations
|
||||
: target,
|
||||
key,
|
||||
receiver
|
||||
)
|
||||
}
|
||||
|
||||
export const mutableCollectionHandlers: ProxyHandler<any> = {
|
||||
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
|
||||
get: createInstrumentationGetter(mutableInstrumentations)
|
||||
}
|
||||
|
||||
export const readonlyCollectionHandlers: ProxyHandler<any> = {
|
||||
export const readonlyCollectionHandlers: ProxyHandler<CollectionTypes> = {
|
||||
get: createInstrumentationGetter(readonlyInstrumentations)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export function computed<T>(
|
||||
): WritableComputedRef<T>
|
||||
export function computed<T>(
|
||||
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
|
||||
): any {
|
||||
) {
|
||||
const isReadonly = isFunction(getterOrOptions)
|
||||
const getter = isReadonly
|
||||
? (getterOrOptions as ComputedGetter<T>)
|
||||
|
||||
@@ -2,11 +2,9 @@ import { OperationTypes } from './operations'
|
||||
import { Dep, targetMap } from './reactive'
|
||||
import { EMPTY_OBJ, extend } from '@vue/shared'
|
||||
|
||||
export const effectSymbol = Symbol(__DEV__ ? 'effect' : void 0)
|
||||
|
||||
export interface ReactiveEffect<T = any> {
|
||||
(): T
|
||||
[effectSymbol]: true
|
||||
_isEffect: true
|
||||
active: boolean
|
||||
raw: () => T
|
||||
deps: Array<Dep>
|
||||
@@ -26,11 +24,17 @@ export interface ReactiveEffectOptions {
|
||||
onStop?: () => void
|
||||
}
|
||||
|
||||
export interface DebuggerEvent {
|
||||
type DebuggerEvent = {
|
||||
effect: ReactiveEffect
|
||||
target: any
|
||||
target: object
|
||||
type: OperationTypes
|
||||
key: string | symbol | undefined
|
||||
key: any
|
||||
} & DebuggerEventExtraInfo
|
||||
|
||||
export interface DebuggerEventExtraInfo {
|
||||
newValue?: any
|
||||
oldValue?: any
|
||||
oldTarget?: Map<any, any> | Set<any>
|
||||
}
|
||||
|
||||
export const effectStack: ReactiveEffect[] = []
|
||||
@@ -38,7 +42,7 @@ export const effectStack: ReactiveEffect[] = []
|
||||
export const ITERATE_KEY = Symbol('iterate')
|
||||
|
||||
export function isEffect(fn: any): fn is ReactiveEffect {
|
||||
return fn != null && fn[effectSymbol] === true
|
||||
return fn != null && fn._isEffect === true
|
||||
}
|
||||
|
||||
export function effect<T = any>(
|
||||
@@ -69,10 +73,10 @@ function createReactiveEffect<T = any>(
|
||||
fn: () => T,
|
||||
options: ReactiveEffectOptions
|
||||
): ReactiveEffect<T> {
|
||||
const effect = function reactiveEffect(...args: any[]): any {
|
||||
const effect = function reactiveEffect(...args: unknown[]): unknown {
|
||||
return run(effect, fn, args)
|
||||
} as ReactiveEffect
|
||||
effect[effectSymbol] = true
|
||||
effect._isEffect = true
|
||||
effect.active = true
|
||||
effect.raw = fn
|
||||
effect.scheduler = options.scheduler
|
||||
@@ -84,7 +88,7 @@ function createReactiveEffect<T = any>(
|
||||
return effect
|
||||
}
|
||||
|
||||
function run(effect: ReactiveEffect, fn: Function, args: any[]): any {
|
||||
function run(effect: ReactiveEffect, fn: Function, args: unknown[]): unknown {
|
||||
if (!effect.active) {
|
||||
return fn(...args)
|
||||
}
|
||||
@@ -119,11 +123,7 @@ export function resumeTracking() {
|
||||
shouldTrack = true
|
||||
}
|
||||
|
||||
export function track(
|
||||
target: any,
|
||||
type: OperationTypes,
|
||||
key?: string | symbol
|
||||
) {
|
||||
export function track(target: object, type: OperationTypes, key?: unknown) {
|
||||
if (!shouldTrack || effectStack.length === 0) {
|
||||
return
|
||||
}
|
||||
@@ -154,10 +154,10 @@ export function track(
|
||||
}
|
||||
|
||||
export function trigger(
|
||||
target: any,
|
||||
target: object,
|
||||
type: OperationTypes,
|
||||
key?: string | symbol,
|
||||
extraInfo?: any
|
||||
key?: unknown,
|
||||
extraInfo?: DebuggerEventExtraInfo
|
||||
) {
|
||||
const depsMap = targetMap.get(target)
|
||||
if (depsMap === void 0) {
|
||||
@@ -209,23 +209,19 @@ function addRunners(
|
||||
|
||||
function scheduleRun(
|
||||
effect: ReactiveEffect,
|
||||
target: any,
|
||||
target: object,
|
||||
type: OperationTypes,
|
||||
key: string | symbol | undefined,
|
||||
extraInfo: any
|
||||
key: unknown,
|
||||
extraInfo?: DebuggerEventExtraInfo
|
||||
) {
|
||||
if (__DEV__ && effect.onTrigger) {
|
||||
effect.onTrigger(
|
||||
extend(
|
||||
{
|
||||
effect,
|
||||
target,
|
||||
key,
|
||||
type
|
||||
},
|
||||
extraInfo
|
||||
)
|
||||
)
|
||||
const event: DebuggerEvent = {
|
||||
effect,
|
||||
target,
|
||||
key,
|
||||
type
|
||||
}
|
||||
effect.onTrigger(extraInfo ? extend(event, extraInfo) : event)
|
||||
}
|
||||
if (effect.scheduler !== void 0) {
|
||||
effect.scheduler(effect)
|
||||
|
||||
@@ -13,7 +13,7 @@ import { makeMap } from '@vue/shared'
|
||||
// which maintains a Set of subscribers, but we simply store them as
|
||||
// raw Sets to reduce memory overhead.
|
||||
export type Dep = Set<ReactiveEffect>
|
||||
export type KeyToDepMap = Map<string | symbol, Dep>
|
||||
export type KeyToDepMap = Map<any, Dep>
|
||||
export const targetMap = new WeakMap<any, KeyToDepMap>()
|
||||
|
||||
// WeakMaps that store {raw <-> observed} pairs.
|
||||
@@ -83,7 +83,7 @@ export function readonly<T extends object>(
|
||||
}
|
||||
|
||||
function createReactiveObject(
|
||||
target: any,
|
||||
target: unknown,
|
||||
toProxy: WeakMap<any, any>,
|
||||
toRaw: WeakMap<any, any>,
|
||||
baseHandlers: ProxyHandler<any>,
|
||||
@@ -120,11 +120,11 @@ function createReactiveObject(
|
||||
return observed
|
||||
}
|
||||
|
||||
export function isReactive(value: any): boolean {
|
||||
export function isReactive(value: unknown): boolean {
|
||||
return reactiveToRaw.has(value) || readonlyToRaw.has(value)
|
||||
}
|
||||
|
||||
export function isReadonly(value: any): boolean {
|
||||
export function isReadonly(value: unknown): boolean {
|
||||
return readonlyToRaw.has(value)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,37 +3,39 @@ import { OperationTypes } from './operations'
|
||||
import { isObject } from '@vue/shared'
|
||||
import { reactive } from './reactive'
|
||||
import { ComputedRef } from './computed'
|
||||
import { CollectionTypes } from './collectionHandlers'
|
||||
|
||||
export interface Ref<T = any> {
|
||||
_isRef: true
|
||||
value: UnwrapRef<T>
|
||||
}
|
||||
|
||||
const convert = (val: any): any => (isObject(val) ? reactive(val) : val)
|
||||
const convert = <T extends unknown>(val: T): T =>
|
||||
isObject(val) ? reactive(val) : val
|
||||
|
||||
export function ref<T extends Ref>(raw: T): T
|
||||
export function ref<T>(raw: T): Ref<T>
|
||||
export function ref(raw: any) {
|
||||
export function ref(raw: unknown) {
|
||||
if (isRef(raw)) {
|
||||
return raw
|
||||
}
|
||||
raw = convert(raw)
|
||||
const v = {
|
||||
const r = {
|
||||
_isRef: true,
|
||||
get value() {
|
||||
track(v, OperationTypes.GET, '')
|
||||
track(r, OperationTypes.GET, '')
|
||||
return raw
|
||||
},
|
||||
set value(newVal) {
|
||||
raw = convert(newVal)
|
||||
trigger(v, OperationTypes.SET, '')
|
||||
trigger(r, OperationTypes.SET, '')
|
||||
}
|
||||
}
|
||||
return v as Ref
|
||||
return r as Ref
|
||||
}
|
||||
|
||||
export function isRef(v: any): v is Ref {
|
||||
return v ? v._isRef === true : false
|
||||
export function isRef(r: any): r is Ref {
|
||||
return r ? r._isRef === true : false
|
||||
}
|
||||
|
||||
export function toRefs<T extends object>(
|
||||
@@ -61,13 +63,6 @@ function toProxyRef<T extends object, K extends keyof T>(
|
||||
}
|
||||
}
|
||||
|
||||
type BailTypes =
|
||||
| Function
|
||||
| Map<any, any>
|
||||
| Set<any>
|
||||
| WeakMap<any, any>
|
||||
| WeakSet<any>
|
||||
|
||||
// Recursively unwraps nested value bindings.
|
||||
export type UnwrapRef<T> = {
|
||||
cRef: T extends ComputedRef<infer V> ? UnwrapRef<V> : T
|
||||
@@ -77,9 +72,11 @@ export type UnwrapRef<T> = {
|
||||
}[T extends ComputedRef<any>
|
||||
? 'cRef'
|
||||
: T extends Ref
|
||||
? 'ref'
|
||||
: T extends Array<any>
|
||||
? 'array'
|
||||
: T extends BailTypes
|
||||
? 'ref' // bail out on types that shouldn't be unwrapped
|
||||
: T extends object ? 'object' : 'ref']
|
||||
? 'ref'
|
||||
: T extends Array<any>
|
||||
? 'array'
|
||||
: T extends Function | CollectionTypes
|
||||
? 'ref' // bail out on types that shouldn't be unwrapped
|
||||
: T extends object
|
||||
? 'object'
|
||||
: 'ref']
|
||||
|
||||
Reference in New Issue
Block a user