types: massive refactor

This commit is contained in:
Evan You 2019-10-22 11:26:48 -04:00
parent 522beaa766
commit b5886189ba
21 changed files with 308 additions and 258 deletions

View File

@ -12,7 +12,7 @@ const builtInSymbols = new Set(
) )
function createGetter(isReadonly: boolean) { 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) const res = Reflect.get(target, key, receiver)
if (isSymbol(key) && builtInSymbols.has(key)) { if (isSymbol(key) && builtInSymbols.has(key)) {
return res return res
@ -32,13 +32,13 @@ function createGetter(isReadonly: boolean) {
} }
function set( function set(
target: any, target: object,
key: string | symbol, key: string | symbol,
value: any, value: unknown,
receiver: any receiver: object
): boolean { ): boolean {
value = toRaw(value) value = toRaw(value)
const oldValue = target[key] const oldValue = (target as any)[key]
if (isRef(oldValue) && !isRef(value)) { if (isRef(oldValue) && !isRef(value)) {
oldValue.value = value oldValue.value = value
return true return true
@ -66,9 +66,9 @@ function set(
return result return result
} }
function deleteProperty(target: any, key: string | symbol): boolean { function deleteProperty(target: object, key: string | symbol): boolean {
const hadKey = hasOwn(target, key) const hadKey = hasOwn(target, key)
const oldValue = target[key] const oldValue = (target as any)[key]
const result = Reflect.deleteProperty(target, key) const result = Reflect.deleteProperty(target, key)
if (result && hadKey) { if (result && hadKey) {
/* istanbul ignore else */ /* istanbul ignore else */
@ -81,18 +81,18 @@ function deleteProperty(target: any, key: string | symbol): boolean {
return result return result
} }
function has(target: any, key: string | symbol): boolean { function has(target: object, key: string | symbol): boolean {
const result = Reflect.has(target, key) const result = Reflect.has(target, key)
track(target, OperationTypes.HAS, key) track(target, OperationTypes.HAS, key)
return result return result
} }
function ownKeys(target: any): (string | number | symbol)[] { function ownKeys(target: object): (string | number | symbol)[] {
track(target, OperationTypes.ITERATE) track(target, OperationTypes.ITERATE)
return Reflect.ownKeys(target) return Reflect.ownKeys(target)
} }
export const mutableHandlers: ProxyHandler<any> = { export const mutableHandlers: ProxyHandler<object> = {
get: createGetter(false), get: createGetter(false),
set, set,
deleteProperty, deleteProperty,
@ -100,10 +100,15 @@ export const mutableHandlers: ProxyHandler<any> = {
ownKeys ownKeys
} }
export const readonlyHandlers: ProxyHandler<any> = { export const readonlyHandlers: ProxyHandler<object> = {
get: createGetter(true), 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 (LOCKED) {
if (__DEV__) { if (__DEV__) {
console.warn( 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 (LOCKED) {
if (__DEV__) { if (__DEV__) {
console.warn( console.warn(

View File

@ -4,43 +4,56 @@ import { OperationTypes } from './operations'
import { LOCKED } from './lock' import { LOCKED } from './lock'
import { isObject, capitalize, hasOwn } from '@vue/shared' import { isObject, capitalize, hasOwn } from '@vue/shared'
const toReactive = (value: any) => (isObject(value) ? reactive(value) : value) export type CollectionTypes = IterableCollections | WeakCollections
const toReadonly = (value: any) => (isObject(value) ? readonly(value) : value)
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) target = toRaw(target)
key = toRaw(key) key = toRaw(key)
const proto: any = Reflect.getPrototypeOf(target)
track(target, OperationTypes.GET, key) track(target, OperationTypes.GET, key)
const res = proto.get.call(target, key) return wrap(getProto(target).get.call(target, key))
return wrap(res)
} }
function has(this: any, key: any): boolean { function has(this: CollectionTypes, key: unknown): boolean {
const target = toRaw(this) const target = toRaw(this)
key = toRaw(key) key = toRaw(key)
const proto: any = Reflect.getPrototypeOf(target)
track(target, OperationTypes.HAS, key) 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) target = toRaw(target)
const proto = Reflect.getPrototypeOf(target)
track(target, OperationTypes.ITERATE) 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) value = toRaw(value)
const target = toRaw(this) const target = toRaw(this)
const proto: any = Reflect.getPrototypeOf(this) const proto = getProto(target)
const hadKey = proto.has.call(target, value) const hadKey = proto.has.call(target, value)
const result = proto.add.call(target, value) const result = proto.add.call(target, value)
if (!hadKey) { if (!hadKey) {
/* istanbul ignore else */ /* istanbul ignore else */
if (__DEV__) { if (__DEV__) {
trigger(target, OperationTypes.ADD, value, { value }) trigger(target, OperationTypes.ADD, value, { newValue: value })
} else { } else {
trigger(target, OperationTypes.ADD, value) trigger(target, OperationTypes.ADD, value)
} }
@ -48,10 +61,10 @@ function add(this: any, value: any) {
return result return result
} }
function set(this: any, key: any, value: any) { function set(this: MapTypes, key: unknown, value: unknown) {
value = toRaw(value) value = toRaw(value)
const target = toRaw(this) const target = toRaw(this)
const proto: any = Reflect.getPrototypeOf(this) const proto = getProto(target)
const hadKey = proto.has.call(target, key) const hadKey = proto.has.call(target, key)
const oldValue = proto.get.call(target, key) const oldValue = proto.get.call(target, key)
const result = proto.set.call(target, key, value) const result = proto.set.call(target, key, value)
@ -75,9 +88,9 @@ function set(this: any, key: any, value: any) {
return result return result
} }
function deleteEntry(this: any, key: any) { function deleteEntry(this: CollectionTypes, key: unknown) {
const target = toRaw(this) const target = toRaw(this)
const proto: any = Reflect.getPrototypeOf(this) const proto = getProto(target)
const hadKey = proto.has.call(target, key) const hadKey = proto.has.call(target, key)
const oldValue = proto.get ? proto.get.call(target, key) : undefined const oldValue = proto.get ? proto.get.call(target, key) : undefined
// forward the operation before queueing reactions // forward the operation before queueing reactions
@ -93,13 +106,16 @@ function deleteEntry(this: any, key: any) {
return result return result
} }
function clear(this: any) { function clear(this: IterableCollections) {
const target = toRaw(this) const target = toRaw(this)
const proto: any = Reflect.getPrototypeOf(this)
const hadItems = target.size !== 0 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 // forward the operation before queueing reactions
const result = proto.clear.call(target) const result = getProto(target).clear.call(target)
if (hadItems) { if (hadItems) {
/* istanbul ignore else */ /* istanbul ignore else */
if (__DEV__) { if (__DEV__) {
@ -112,30 +128,32 @@ function clear(this: any) {
} }
function createForEach(isReadonly: boolean) { 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 observed = this
const target = toRaw(observed) const target = toRaw(observed)
const proto: any = Reflect.getPrototypeOf(target)
const wrap = isReadonly ? toReadonly : toReactive const wrap = isReadonly ? toReadonly : toReactive
track(target, OperationTypes.ITERATE) track(target, OperationTypes.ITERATE)
// important: create sure the callback is // important: create sure the callback is
// 1. invoked with the reactive map as `this` and 3rd arg // 1. invoked with the reactive map as `this` and 3rd arg
// 2. the value received should be a corresponding reactive/readonly. // 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 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) { function createIterableMethod(method: string | symbol, isReadonly: boolean) {
return function(this: any, ...args: any[]) { return function(this: IterableCollections, ...args: unknown[]) {
const target = toRaw(this) const target = toRaw(this)
const proto: any = Reflect.getPrototypeOf(target)
const isPair = const isPair =
method === 'entries' || method === 'entries' ||
(method === Symbol.iterator && target instanceof Map) (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 const wrap = isReadonly ? toReadonly : toReactive
track(target, OperationTypes.ITERATE) track(target, OperationTypes.ITERATE)
// return a wrapped iterator which returns observed versions of the // return a wrapped iterator which returns observed versions of the
@ -163,7 +181,7 @@ function createReadonlyMethod(
method: Function, method: Function,
type: OperationTypes type: OperationTypes
): Function { ): Function {
return function(this: any, ...args: any[]) { return function(this: CollectionTypes, ...args: unknown[]) {
if (LOCKED) { if (LOCKED) {
if (__DEV__) { if (__DEV__) {
const key = args[0] ? `on key "${args[0]}" ` : `` const key = args[0] ? `on key "${args[0]}" ` : ``
@ -179,11 +197,11 @@ function createReadonlyMethod(
} }
} }
const mutableInstrumentations: any = { const mutableInstrumentations: Record<string, Function> = {
get(key: any) { get(this: MapTypes, key: unknown) {
return get(this, key, toReactive) return get(this, key, toReactive)
}, },
get size() { get size(this: IterableCollections) {
return size(this) return size(this)
}, },
has, has,
@ -194,11 +212,11 @@ const mutableInstrumentations: any = {
forEach: createForEach(false) forEach: createForEach(false)
} }
const readonlyInstrumentations: any = { const readonlyInstrumentations: Record<string, Function> = {
get(key: any) { get(this: MapTypes, key: unknown) {
return get(this, key, toReadonly) return get(this, key, toReadonly)
}, },
get size() { get size(this: IterableCollections) {
return size(this) return size(this)
}, },
has, has,
@ -211,26 +229,37 @@ const readonlyInstrumentations: any = {
const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator] const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]
iteratorMethods.forEach(method => { iteratorMethods.forEach(method => {
mutableInstrumentations[method] = createIterableMethod(method, false) mutableInstrumentations[method as string] = createIterableMethod(
readonlyInstrumentations[method] = createIterableMethod(method, true) method,
false
)
readonlyInstrumentations[method as string] = createIterableMethod(
method,
true
)
}) })
function createInstrumentationGetter(instrumentations: any) { function createInstrumentationGetter(
return function getInstrumented( instrumentations: Record<string, Function>
target: any,
key: string | symbol,
receiver: any
) { ) {
target = return (
hasOwn(instrumentations, key) && key in target ? instrumentations : target target: CollectionTypes,
return Reflect.get(target, key, receiver) key: string | symbol,
} 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) get: createInstrumentationGetter(mutableInstrumentations)
} }
export const readonlyCollectionHandlers: ProxyHandler<any> = { export const readonlyCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: createInstrumentationGetter(readonlyInstrumentations) get: createInstrumentationGetter(readonlyInstrumentations)
} }

View File

@ -24,7 +24,7 @@ export function computed<T>(
): WritableComputedRef<T> ): WritableComputedRef<T>
export function computed<T>( export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T> getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
): any { ) {
const isReadonly = isFunction(getterOrOptions) const isReadonly = isFunction(getterOrOptions)
const getter = isReadonly const getter = isReadonly
? (getterOrOptions as ComputedGetter<T>) ? (getterOrOptions as ComputedGetter<T>)

View File

@ -2,11 +2,9 @@ import { OperationTypes } from './operations'
import { Dep, targetMap } from './reactive' import { Dep, targetMap } from './reactive'
import { EMPTY_OBJ, extend } from '@vue/shared' import { EMPTY_OBJ, extend } from '@vue/shared'
export const effectSymbol = Symbol(__DEV__ ? 'effect' : void 0)
export interface ReactiveEffect<T = any> { export interface ReactiveEffect<T = any> {
(): T (): T
[effectSymbol]: true _isEffect: true
active: boolean active: boolean
raw: () => T raw: () => T
deps: Array<Dep> deps: Array<Dep>
@ -26,11 +24,17 @@ export interface ReactiveEffectOptions {
onStop?: () => void onStop?: () => void
} }
export interface DebuggerEvent { type DebuggerEvent = {
effect: ReactiveEffect effect: ReactiveEffect
target: any target: object
type: OperationTypes 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[] = [] export const effectStack: ReactiveEffect[] = []
@ -38,7 +42,7 @@ export const effectStack: ReactiveEffect[] = []
export const ITERATE_KEY = Symbol('iterate') export const ITERATE_KEY = Symbol('iterate')
export function isEffect(fn: any): fn is ReactiveEffect { 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>( export function effect<T = any>(
@ -69,10 +73,10 @@ function createReactiveEffect<T = any>(
fn: () => T, fn: () => T,
options: ReactiveEffectOptions options: ReactiveEffectOptions
): ReactiveEffect<T> { ): ReactiveEffect<T> {
const effect = function reactiveEffect(...args: any[]): any { const effect = function reactiveEffect(...args: unknown[]): unknown {
return run(effect, fn, args) return run(effect, fn, args)
} as ReactiveEffect } as ReactiveEffect
effect[effectSymbol] = true effect._isEffect = true
effect.active = true effect.active = true
effect.raw = fn effect.raw = fn
effect.scheduler = options.scheduler effect.scheduler = options.scheduler
@ -84,7 +88,7 @@ function createReactiveEffect<T = any>(
return effect return effect
} }
function run(effect: ReactiveEffect, fn: Function, args: any[]): any { function run(effect: ReactiveEffect, fn: Function, args: unknown[]): unknown {
if (!effect.active) { if (!effect.active) {
return fn(...args) return fn(...args)
} }
@ -119,11 +123,7 @@ export function resumeTracking() {
shouldTrack = true shouldTrack = true
} }
export function track( export function track(target: object, type: OperationTypes, key?: unknown) {
target: any,
type: OperationTypes,
key?: string | symbol
) {
if (!shouldTrack || effectStack.length === 0) { if (!shouldTrack || effectStack.length === 0) {
return return
} }
@ -154,10 +154,10 @@ export function track(
} }
export function trigger( export function trigger(
target: any, target: object,
type: OperationTypes, type: OperationTypes,
key?: string | symbol, key?: unknown,
extraInfo?: any extraInfo?: DebuggerEventExtraInfo
) { ) {
const depsMap = targetMap.get(target) const depsMap = targetMap.get(target)
if (depsMap === void 0) { if (depsMap === void 0) {
@ -209,23 +209,19 @@ function addRunners(
function scheduleRun( function scheduleRun(
effect: ReactiveEffect, effect: ReactiveEffect,
target: any, target: object,
type: OperationTypes, type: OperationTypes,
key: string | symbol | undefined, key: unknown,
extraInfo: any extraInfo?: DebuggerEventExtraInfo
) { ) {
if (__DEV__ && effect.onTrigger) { if (__DEV__ && effect.onTrigger) {
effect.onTrigger( const event: DebuggerEvent = {
extend(
{
effect, effect,
target, target,
key, key,
type type
}, }
extraInfo effect.onTrigger(extraInfo ? extend(event, extraInfo) : event)
)
)
} }
if (effect.scheduler !== void 0) { if (effect.scheduler !== void 0) {
effect.scheduler(effect) effect.scheduler(effect)

View File

@ -13,7 +13,7 @@ import { makeMap } from '@vue/shared'
// which maintains a Set of subscribers, but we simply store them as // which maintains a Set of subscribers, but we simply store them as
// raw Sets to reduce memory overhead. // raw Sets to reduce memory overhead.
export type Dep = Set<ReactiveEffect> 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>() export const targetMap = new WeakMap<any, KeyToDepMap>()
// WeakMaps that store {raw <-> observed} pairs. // WeakMaps that store {raw <-> observed} pairs.
@ -83,7 +83,7 @@ export function readonly<T extends object>(
} }
function createReactiveObject( function createReactiveObject(
target: any, target: unknown,
toProxy: WeakMap<any, any>, toProxy: WeakMap<any, any>,
toRaw: WeakMap<any, any>, toRaw: WeakMap<any, any>,
baseHandlers: ProxyHandler<any>, baseHandlers: ProxyHandler<any>,
@ -120,11 +120,11 @@ function createReactiveObject(
return observed return observed
} }
export function isReactive(value: any): boolean { export function isReactive(value: unknown): boolean {
return reactiveToRaw.has(value) || readonlyToRaw.has(value) return reactiveToRaw.has(value) || readonlyToRaw.has(value)
} }
export function isReadonly(value: any): boolean { export function isReadonly(value: unknown): boolean {
return readonlyToRaw.has(value) return readonlyToRaw.has(value)
} }

View File

@ -3,37 +3,39 @@ import { OperationTypes } from './operations'
import { isObject } from '@vue/shared' import { isObject } from '@vue/shared'
import { reactive } from './reactive' import { reactive } from './reactive'
import { ComputedRef } from './computed' import { ComputedRef } from './computed'
import { CollectionTypes } from './collectionHandlers'
export interface Ref<T = any> { export interface Ref<T = any> {
_isRef: true _isRef: true
value: UnwrapRef<T> 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 extends Ref>(raw: T): T
export function ref<T>(raw: T): Ref<T> export function ref<T>(raw: T): Ref<T>
export function ref(raw: any) { export function ref(raw: unknown) {
if (isRef(raw)) { if (isRef(raw)) {
return raw return raw
} }
raw = convert(raw) raw = convert(raw)
const v = { const r = {
_isRef: true, _isRef: true,
get value() { get value() {
track(v, OperationTypes.GET, '') track(r, OperationTypes.GET, '')
return raw return raw
}, },
set value(newVal) { set value(newVal) {
raw = convert(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 { export function isRef(r: any): r is Ref {
return v ? v._isRef === true : false return r ? r._isRef === true : false
} }
export function toRefs<T extends object>( 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. // Recursively unwraps nested value bindings.
export type UnwrapRef<T> = { export type UnwrapRef<T> = {
cRef: T extends ComputedRef<infer V> ? UnwrapRef<V> : T cRef: T extends ComputedRef<infer V> ? UnwrapRef<V> : T
@ -80,6 +75,8 @@ export type UnwrapRef<T> = {
? 'ref' ? 'ref'
: T extends Array<any> : T extends Array<any>
? 'array' ? 'array'
: T extends BailTypes : T extends Function | CollectionTypes
? 'ref' // bail out on types that shouldn't be unwrapped ? 'ref' // bail out on types that shouldn't be unwrapped
: T extends object ? 'object' : 'ref'] : T extends object
? 'object'
: 'ref']

View File

@ -77,6 +77,6 @@ export function createComponent<
} }
// implementation, close to no-op // implementation, close to no-op
export function createComponent(options: any) { export function createComponent(options: unknown) {
return isFunction(options) ? { setup: options } : options return isFunction(options) ? { setup: options } : options
} }

View File

@ -27,7 +27,10 @@ export function provide<T>(key: InjectionKey<T> | string, value: T) {
export function inject<T>(key: InjectionKey<T> | string): T | undefined export function inject<T>(key: InjectionKey<T> | string): T | undefined
export function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T export function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T
export function inject(key: InjectionKey<any> | string, defaultValue?: any) { export function inject(
key: InjectionKey<any> | string,
defaultValue?: unknown
) {
if (currentInstance) { if (currentInstance) {
const provides = currentInstance.provides const provides = currentInstance.provides
if (key in provides) { if (key in provides) {

View File

@ -16,7 +16,7 @@ function injectHook(
target: ComponentInternalInstance | null target: ComponentInternalInstance | null
) { ) {
if (target) { if (target) {
;(target[type] || (target[type] = [])).push((...args: any[]) => { ;(target[type] || (target[type] = [])).push((...args: unknown[]) => {
if (target.isUnmounted) { if (target.isUnmounted) {
return return
} }

View File

@ -15,7 +15,7 @@ import {
NOOP NOOP
} from '@vue/shared' } from '@vue/shared'
import { computed } from './apiReactivity' import { computed } from './apiReactivity'
import { watch, WatchOptions, CleanupRegistrator } from './apiWatch' import { watch, WatchOptions, WatchHandler } from './apiWatch'
import { provide, inject } from './apiInject' import { provide, inject } from './apiInject'
import { import {
onBeforeMount, onBeforeMount,
@ -40,7 +40,7 @@ import { Directive } from './directives'
import { ComponentPublicInstance } from './componentProxy' import { ComponentPublicInstance } from './componentProxy'
import { warn } from './warning' import { warn } from './warning'
interface ComponentOptionsBase< export interface ComponentOptionsBase<
Props, Props,
RawBindings, RawBindings,
D, D,
@ -119,12 +119,6 @@ export type ExtractComputedReturns<T extends any> = {
: ReturnType<T[key]> : ReturnType<T[key]>
} }
export type WatchHandler<T = any> = (
val: T,
oldVal: T,
onCleanup: CleanupRegistrator
) => any
type ComponentWatchOptions = Record< type ComponentWatchOptions = Record<
string, string,
string | WatchHandler | { handler: WatchHandler } & WatchOptions string | WatchHandler | { handler: WatchHandler } & WatchOptions
@ -134,7 +128,7 @@ type ComponentInjectOptions =
| string[] | string[]
| Record< | Record<
string | symbol, string | symbol,
string | symbol | { from: string | symbol; default?: any } string | symbol | { from: string | symbol; default?: unknown }
> >
// TODO type inference for these options // TODO type inference for these options

View File

@ -21,7 +21,12 @@ import {
} from './errorHandling' } from './errorHandling'
import { onBeforeUnmount } from './apiLifecycle' import { onBeforeUnmount } from './apiLifecycle'
import { queuePostRenderEffect } from './createRenderer' import { queuePostRenderEffect } from './createRenderer'
import { WatchHandler } from './apiOptions'
export type WatchHandler<T = any> = (
value: T,
oldValue: T,
onCleanup: CleanupRegistrator
) => any
export interface WatchOptions { export interface WatchOptions {
lazy?: boolean lazy?: boolean
@ -58,11 +63,7 @@ export function watch<T>(
// overload #3: array of multiple sources + cb // overload #3: array of multiple sources + cb
export function watch<T extends readonly WatcherSource<unknown>[]>( export function watch<T extends readonly WatcherSource<unknown>[]>(
sources: T, sources: T,
cb: ( cb: WatchHandler<MapSources<T>>,
newValues: MapSources<T>,
oldValues: MapSources<T>,
onCleanup: CleanupRegistrator
) => any,
options?: WatchOptions options?: WatchOptions
): StopHandle ): StopHandle
@ -84,9 +85,7 @@ export function watch<T = any>(
function doWatch( function doWatch(
source: WatcherSource | WatcherSource[] | SimpleEffect, source: WatcherSource | WatcherSource[] | SimpleEffect,
cb: cb: WatchHandler | null,
| ((newValue: any, oldValue: any, onCleanup: CleanupRegistrator) => any)
| null,
{ lazy, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ { lazy, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
): StopHandle { ): StopHandle {
const instance = currentInstance const instance = currentInstance
@ -95,8 +94,7 @@ function doWatch(
let getter: () => any let getter: () => any
if (isArray(source)) { if (isArray(source)) {
getter = () => getter = () =>
source.map( source.map(s =>
s =>
isRef(s) isRef(s)
? s.value ? s.value
: callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER) : callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
@ -218,7 +216,7 @@ export function instanceWatch(
return stop return stop
} }
function traverse(value: any, seen: Set<any> = new Set()) { function traverse(value: unknown, seen: Set<unknown> = new Set()) {
if (!isObject(value) || seen.has(value)) { if (!isObject(value) || seen.has(value)) {
return return
} }

View File

@ -60,7 +60,7 @@ export const enum LifecycleHooks {
ERROR_CAPTURED = 'ec' ERROR_CAPTURED = 'ec'
} }
export type Emit = ((event: string, ...args: unknown[]) => void) export type Emit = (event: string, ...args: unknown[]) => void
export interface SetupContext { export interface SetupContext {
attrs: Data attrs: Data
@ -86,13 +86,13 @@ export interface ComponentInternalInstance {
accessCache: Data | null accessCache: Data | null
// cache for render function values that rely on _ctx but won't need updates // cache for render function values that rely on _ctx but won't need updates
// after initialized (e.g. inline handlers) // after initialized (e.g. inline handlers)
renderCache: any[] | null renderCache: Function[] | null
components: Record<string, Component> components: Record<string, Component>
directives: Record<string, Directive> directives: Record<string, Directive>
asyncDep: Promise<any> | null asyncDep: Promise<any> | null
asyncResult: any asyncResult: unknown
asyncResolved: boolean asyncResolved: boolean
// the rest are only for stateful components // the rest are only for stateful components

View File

@ -30,12 +30,12 @@ interface PropOptions<T = any> {
type?: PropType<T> | true | null type?: PropType<T> | true | null
required?: boolean required?: boolean
default?: T | null | undefined | (() => T | null | undefined) default?: T | null | undefined | (() => T | null | undefined)
validator?(value: any): boolean validator?(value: unknown): boolean
} }
export type PropType<T> = PropConstructor<T> | PropConstructor<T>[] export type PropType<T> = PropConstructor<T> | PropConstructor<T>[]
type PropConstructor<T> = { new (...args: any[]): T & object } | { (): T } type PropConstructor<T = any> = { new (...args: any[]): T & object } | { (): T }
type RequiredKeys<T, MakeDefaultRequired> = { type RequiredKeys<T, MakeDefaultRequired> = {
[K in keyof T]: T[K] extends [K in keyof T]: T[K] extends
@ -56,7 +56,9 @@ type InferPropType<T> = T extends null
? any // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` ? any // somehow `ObjectConstructor` when inferred from { (): T } becomes `any`
: T extends ObjectConstructor | { type: ObjectConstructor } : T extends ObjectConstructor | { type: ObjectConstructor }
? { [key: string]: any } ? { [key: string]: any }
: T extends Prop<infer V> ? V : T : T extends Prop<infer V>
? V
: T
export type ExtractPropTypes< export type ExtractPropTypes<
O, O,
@ -96,7 +98,7 @@ type NormalizedPropsOptions = Record<string, NormalizedProp>
export function resolveProps( export function resolveProps(
instance: ComponentInternalInstance, instance: ComponentInternalInstance,
rawProps: any, rawProps: Data | null,
_options: ComponentPropsOptions | void _options: ComponentPropsOptions | void
) { ) {
const hasDeclaredProps = _options != null const hasDeclaredProps = _options != null
@ -105,18 +107,18 @@ export function resolveProps(
return return
} }
const props: any = {} const props: Data = {}
let attrs: any = void 0 let attrs: Data | undefined = void 0
// update the instance propsProxy (passed to setup()) to trigger potential // update the instance propsProxy (passed to setup()) to trigger potential
// changes // changes
const propsProxy = instance.propsProxy const propsProxy = instance.propsProxy
const setProp = propsProxy const setProp = propsProxy
? (key: string, val: any) => { ? (key: string, val: unknown) => {
props[key] = val props[key] = val
propsProxy[key] = val propsProxy[key] = val
} }
: (key: string, val: any) => { : (key: string, val: unknown) => {
props[key] = val props[key] = val
} }
@ -192,7 +194,7 @@ export function resolveProps(
instance.attrs = options instance.attrs = options
? __DEV__ && attrs != null ? __DEV__ && attrs != null
? readonly(attrs) ? readonly(attrs)
: attrs : attrs!
: instance.props : instance.props
} }
@ -279,8 +281,8 @@ type AssertionResult = {
function validateProp( function validateProp(
name: string, name: string,
value: any, value: unknown,
prop: PropOptions<any>, prop: PropOptions,
isAbsent: boolean isAbsent: boolean
) { ) {
const { type, required, validator } = prop const { type, required, validator } = prop
@ -317,7 +319,7 @@ function validateProp(
const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/ const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/
function assertType(value: any, type: PropConstructor<any>): AssertionResult { function assertType(value: unknown, type: PropConstructor): AssertionResult {
let valid let valid
const expectedType = getType(type) const expectedType = getType(type)
if (simpleCheckRE.test(expectedType)) { if (simpleCheckRE.test(expectedType)) {
@ -342,7 +344,7 @@ function assertType(value: any, type: PropConstructor<any>): AssertionResult {
function getInvalidTypeMessage( function getInvalidTypeMessage(
name: string, name: string,
value: any, value: unknown,
expectedTypes: string[] expectedTypes: string[]
): string { ): string {
let message = let message =
@ -368,7 +370,7 @@ function getInvalidTypeMessage(
return message return message
} }
function styleValue(value: any, type: string): string { function styleValue(value: unknown, type: string): string {
if (type === 'String') { if (type === 'String') {
return `"${value}"` return `"${value}"`
} else if (type === 'Number') { } else if (type === 'Number') {
@ -378,7 +380,7 @@ function styleValue(value: any, type: string): string {
} }
} }
function toRawType(value: any): string { function toRawType(value: unknown): string {
return toTypeString(value).slice(8, -1) return toTypeString(value).slice(8, -1)
} }

View File

@ -2,7 +2,12 @@ import { ComponentInternalInstance, Data, Emit } from './component'
import { nextTick } from './scheduler' import { nextTick } from './scheduler'
import { instanceWatch } from './apiWatch' import { instanceWatch } from './apiWatch'
import { EMPTY_OBJ, hasOwn, isGloballyWhitelisted } from '@vue/shared' import { EMPTY_OBJ, hasOwn, isGloballyWhitelisted } from '@vue/shared'
import { ExtractComputedReturns } from './apiOptions' import {
ExtractComputedReturns,
ComponentOptionsBase,
ComputedOptions,
MethodOptions
} from './apiOptions'
import { UnwrapRef, ReactiveEffect } from '@vue/reactivity' import { UnwrapRef, ReactiveEffect } from '@vue/reactivity'
import { warn } from './warning' import { warn } from './warning'
@ -12,8 +17,8 @@ export type ComponentPublicInstance<
P = {}, P = {},
B = {}, B = {},
D = {}, D = {},
C = {}, C extends ComputedOptions = {},
M = {}, M extends MethodOptions = {},
PublicProps = P PublicProps = P
> = { > = {
[key: string]: any [key: string]: any
@ -26,7 +31,7 @@ export type ComponentPublicInstance<
$parent: ComponentInternalInstance | null $parent: ComponentInternalInstance | null
$emit: Emit $emit: Emit
$el: any $el: any
$options: any $options: ComponentOptionsBase<P, B, D, C, M>
$forceUpdate: ReactiveEffect $forceUpdate: ReactiveEffect
$nextTick: typeof nextTick $nextTick: typeof nextTick
$watch: typeof instanceWatch $watch: typeof instanceWatch
@ -134,7 +139,10 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
if (__RUNTIME_COMPILE__) { if (__RUNTIME_COMPILE__) {
// this trap is only called in browser-compiled render functions that use // this trap is only called in browser-compiled render functions that use
// `with (this) {}` // `with (this) {}`
PublicInstanceProxyHandlers.has = (_: any, key: string): boolean => { PublicInstanceProxyHandlers.has = (
_: ComponentInternalInstance,
key: string
): boolean => {
return key[0] !== '_' && !isGloballyWhitelisted(key) return key[0] !== '_' && !isGloballyWhitelisted(key)
} }
} }

View File

@ -14,7 +14,8 @@ import {
createComponentInstance, createComponentInstance,
setupStatefulComponent, setupStatefulComponent,
handleSetupResult, handleSetupResult,
Component Component,
Data
} from './component' } from './component'
import { import {
renderComponentRoot, renderComponentRoot,
@ -36,7 +37,8 @@ import {
ReactiveEffectOptions, ReactiveEffectOptions,
isRef, isRef,
Ref, Ref,
toRaw toRaw,
DebuggerEvent
} from '@vue/reactivity' } from '@vue/reactivity'
import { resolveProps } from './componentProps' import { resolveProps } from './componentProps'
import { resolveSlots } from './componentSlots' import { resolveSlots } from './componentSlots'
@ -70,7 +72,7 @@ function isSameType(n1: VNode, n2: VNode): boolean {
return n1.type === n2.type && n1.key === n2.key return n1.type === n2.type && n1.key === n2.key
} }
function invokeHooks(hooks: Function[], arg?: any) { function invokeHooks(hooks: Function[], arg?: DebuggerEvent) {
for (let i = 0; i < hooks.length; i++) { for (let i = 0; i < hooks.length; i++) {
hooks[i](arg) hooks[i](arg)
} }
@ -555,8 +557,8 @@ export function createRenderer<
function patchProps( function patchProps(
el: HostElement, el: HostElement,
vnode: HostVNode, vnode: HostVNode,
oldProps: any, oldProps: Data,
newProps: any, newProps: Data,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null, parentSuspense: HostSuspenseBoundary | null,
isSVG: boolean isSVG: boolean
@ -1160,14 +1162,23 @@ export function createRenderer<
) { ) {
// create reactive effect for rendering // create reactive effect for rendering
let mounted = false let mounted = false
instance.update = effect(function componentEffect() { instance.update = effect(
function componentEffect() {
if (!mounted) { if (!mounted) {
const subTree = (instance.subTree = renderComponentRoot(instance)) const subTree = (instance.subTree = renderComponentRoot(instance))
// beforeMount hook // beforeMount hook
if (instance.bm !== null) { if (instance.bm !== null) {
invokeHooks(instance.bm) invokeHooks(instance.bm)
} }
patch(null, subTree, container, anchor, instance, parentSuspense, isSVG) patch(
null,
subTree,
container,
anchor,
instance,
parentSuspense,
isSVG
)
initialVNode.el = subTree.el initialVNode.el = subTree.el
// mounted hook // mounted hook
if (instance.m !== null) { if (instance.m !== null) {
@ -1225,7 +1236,9 @@ export function createRenderer<
popWarningContext() popWarningContext()
} }
} }
}, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions) },
__DEV__ ? createDevEffectOptions(instance) : prodEffectOptions
)
} }
function updateComponentPreRender( function updateComponentPreRender(

View File

@ -65,7 +65,7 @@ function applyDirective(
props: Record<any, any>, props: Record<any, any>,
instance: ComponentInternalInstance, instance: ComponentInternalInstance,
directive: Directive, directive: Directive,
value?: any, value?: unknown,
arg?: string, arg?: string,
modifiers: DirectiveModifiers = EMPTY_OBJ modifiers: DirectiveModifiers = EMPTY_OBJ
) { ) {

View File

@ -54,9 +54,9 @@ export function callWithErrorHandling(
fn: Function, fn: Function,
instance: ComponentInternalInstance | null, instance: ComponentInternalInstance | null,
type: ErrorTypes, type: ErrorTypes,
args?: any[] args?: unknown[]
) { ) {
let res: any let res
try { try {
res = args ? fn(...args) : fn() res = args ? fn(...args) : fn()
} catch (err) { } catch (err) {
@ -69,7 +69,7 @@ export function callWithAsyncErrorHandling(
fn: Function | Function[], fn: Function | Function[],
instance: ComponentInternalInstance | null, instance: ComponentInternalInstance | null,
type: ErrorTypes, type: ErrorTypes,
args?: any[] args?: unknown[]
) { ) {
if (isFunction(fn)) { if (isFunction(fn)) {
const res = callWithErrorHandling(fn, instance, type, args) const res = callWithErrorHandling(fn, instance, type, args)

View File

@ -2,8 +2,12 @@ import { VNodeChild } from '../vnode'
import { isArray, isString, isObject } from '@vue/shared' import { isArray, isString, isObject } from '@vue/shared'
export function renderList( export function renderList(
source: any, source: unknown,
renderItem: (value: any, key: string | number, index?: number) => VNodeChild renderItem: (
value: unknown,
key: string | number,
index?: number
) => VNodeChild
): VNodeChild[] { ): VNodeChild[] {
let ret: VNodeChild[] let ret: VNodeChild[]
if (isArray(source) || isString(source)) { if (isArray(source) || isString(source)) {

View File

@ -1,3 +1,4 @@
import { Data } from '../component'
import { Slot } from '../componentSlots' import { Slot } from '../componentSlots'
import { import {
VNodeChildren, VNodeChildren,
@ -11,7 +12,7 @@ import { PatchFlags } from '@vue/shared'
export function renderSlot( export function renderSlot(
slots: Record<string, Slot>, slots: Record<string, Slot>,
name: string, name: string,
props: any = {}, props: Data = {},
// this is not a user-facing function, so the fallback is always generated by // this is not a user-facing function, so the fallback is always generated by
// the compiler and guaranteed to be an array // the compiler and guaranteed to be an array
fallback?: VNodeChildren fallback?: VNodeChildren

View File

@ -1,7 +1,7 @@
import { isArray, isPlainObject, objectToString } from '@vue/shared' import { isArray, isPlainObject, objectToString } from '@vue/shared'
// for converting {{ interpolation }} values to displayed strings. // for converting {{ interpolation }} values to displayed strings.
export function toString(val: any): string { export function toString(val: unknown): string {
return val == null return val == null
? '' ? ''
: isArray(val) || (isPlainObject(val) && val.toString === objectToString) : isArray(val) || (isPlainObject(val) && val.toString === objectToString)

View File

@ -117,7 +117,7 @@ export function patchEvent(
} }
function createInvoker( function createInvoker(
initialValue: any, initialValue: EventValue,
instance: ComponentInternalInstance | null instance: ComponentInternalInstance | null
) { ) {
const invoker: Invoker = (e: Event) => { const invoker: Invoker = (e: Event) => {