Evan You 4 лет назад
Родитель
Сommit
450767f3bb

+ 2 - 2
src/core/components/keep-alive.ts

@@ -1,4 +1,4 @@
-import { isRegExp, remove } from 'shared/util'
+import { isRegExp, isArray, remove } from 'shared/util'
 import { getFirstComponentChild } from 'core/vdom/helpers/index'
 import type VNode from 'core/vdom/vnode'
 import type { VNodeComponentOptions } from 'typescript/vnode'
@@ -20,7 +20,7 @@ function matches(
   pattern: string | RegExp | Array<string>,
   name: string
 ): boolean {
-  if (Array.isArray(pattern)) {
+  if (isArray(pattern)) {
     return pattern.indexOf(name) > -1
   } else if (typeof pattern === 'string') {
     return pattern.split(',').indexOf(name) > -1

+ 3 - 2
src/core/instance/events.ts

@@ -2,6 +2,7 @@ import type { Component } from 'typescript/component'
 import {
   tip,
   toArray,
+  isArray,
   hyphenate,
   formatComponentName,
   invokeWithErrorHandling
@@ -62,7 +63,7 @@ export function eventsMixin(Vue: Component) {
     fn: Function
   ): Component {
     const vm: Component = this
-    if (Array.isArray(event)) {
+    if (isArray(event)) {
       for (let i = 0, l = event.length; i < l; i++) {
         vm.$on(event[i], fn)
       }
@@ -99,7 +100,7 @@ export function eventsMixin(Vue: Component) {
       return vm
     }
     // array of events
-    if (Array.isArray(event)) {
+    if (isArray(event)) {
       for (let i = 0, l = event.length; i < l; i++) {
         vm.$off(event[i], fn)
       }

+ 3 - 2
src/core/instance/render-helpers/bind-object-props.ts

@@ -6,7 +6,8 @@ import {
   toObject,
   isReservedAttribute,
   camelize,
-  hyphenate
+  hyphenate,
+  isArray
 } from 'core/util/index'
 import type { VNodeData } from 'typescript/vnode'
 
@@ -25,7 +26,7 @@ export function bindObjectProps(
       __DEV__ &&
         warn('v-bind without argument expects an Object or Array value', this)
     } else {
-      if (Array.isArray(value)) {
+      if (isArray(value)) {
         value = toObject(value)
       }
       let hash

+ 2 - 2
src/core/instance/render-helpers/check-keycodes.ts

@@ -1,8 +1,8 @@
 import config from 'core/config'
-import { hyphenate } from 'shared/util'
+import { hyphenate, isArray } from 'shared/util'
 
 function isKeyNotMatch<T>(expect: T | Array<T>, actual: T): boolean {
-  if (Array.isArray(expect)) {
+  if (isArray(expect)) {
     return expect.indexOf(actual) === -1
   } else {
     return expect !== actual

+ 2 - 2
src/core/instance/render-helpers/render-list.ts

@@ -1,4 +1,4 @@
-import { isObject, isDef, hasSymbol } from 'core/util/index'
+import { isObject, isDef, hasSymbol, isArray } from 'core/util/index'
 import VNode from 'core/vdom/vnode'
 
 /**
@@ -13,7 +13,7 @@ export function renderList(
     l,
     keys,
     key
-  if (Array.isArray(val) || typeof val === 'string') {
+  if (isArray(val) || typeof val === 'string') {
     ret = new Array(val.length)
     for (i = 0, l = val.length; i < l; i++) {
       ret[i] = render(val[i], i)

+ 2 - 1
src/core/instance/render-helpers/render-static.ts

@@ -1,4 +1,5 @@
 import VNode from 'core/vdom/vnode'
+import { isArray } from 'core/util'
 
 /**
  * Runtime helper for rendering static trees.
@@ -38,7 +39,7 @@ export function markOnce(
 }
 
 function markStatic(tree: VNode | Array<VNode>, key: string, isOnce: boolean) {
-  if (Array.isArray(tree)) {
+  if (isArray(tree)) {
     for (let i = 0; i < tree.length; i++) {
       if (tree[i] && typeof tree[i] !== 'string') {
         markStaticNode(tree[i], `${key}_${i}`, isOnce)

+ 2 - 1
src/core/instance/render-helpers/resolve-scoped-slots.ts

@@ -1,4 +1,5 @@
 import type { ScopedSlotsData } from 'typescript/vnode'
+import { isArray } from 'core/util'
 
 export function resolveScopedSlots(
   fns: ScopedSlotsData,
@@ -10,7 +11,7 @@ export function resolveScopedSlots(
   res = res || { $stable: !hasDynamicKeys }
   for (let i = 0; i < fns.length; i++) {
     const slot = fns[i]
-    if (Array.isArray(slot)) {
+    if (isArray(slot)) {
       resolveScopedSlots(slot, res, hasDynamicKeys)
     } else if (slot) {
       // marker for reverse proxying v-slot without scope on this.$slots

+ 4 - 3
src/core/instance/render.ts

@@ -3,7 +3,8 @@ import {
   nextTick,
   emptyObject,
   handleError,
-  defineReactive
+  defineReactive,
+  isArray
 } from '../util/index'
 
 import { createElement } from '../vdom/create-element'
@@ -137,12 +138,12 @@ export function renderMixin(Vue: Component) {
       currentRenderingInstance = null
     }
     // if the returned array contains only a single node, allow it
-    if (Array.isArray(vnode) && vnode.length === 1) {
+    if (isArray(vnode) && vnode.length === 1) {
       vnode = vnode[0]
     }
     // return empty vnode in case the render function errored out
     if (!(vnode instanceof VNode)) {
-      if (__DEV__ && Array.isArray(vnode)) {
+      if (__DEV__ && isArray(vnode)) {
         warn(
           'Multiple root nodes returned from render function. Render function ' +
             'should return a single root node.',

+ 2 - 1
src/core/instance/state.ts

@@ -16,6 +16,7 @@ import {
   bind,
   noop,
   hasOwn,
+  isArray,
   hyphenate,
   isReserved,
   handleError,
@@ -289,7 +290,7 @@ function initMethods(vm: Component, methods: Object) {
 function initWatch(vm: Component, watch: Object) {
   for (const key in watch) {
     const handler = watch[key]
-    if (Array.isArray(handler)) {
+    if (isArray(handler)) {
       for (let i = 0; i < handler.length; i++) {
         createWatcher(vm, key, handler[i])
       }

+ 12 - 7
src/core/observer/dep.ts

@@ -1,28 +1,33 @@
-import type Watcher from './watcher'
 import { remove } from '../util/index'
 import config from '../config'
 
 let uid = 0
 
+export interface DepTarget {
+  id: number
+  addDep(dep: Dep): void
+  update(): void
+}
+
 /**
  * A dep is an observable that can have multiple
  * directives subscribing to it.
  */
 export default class Dep {
-  static target?: Watcher | null
+  static target?: DepTarget | null
   id: number
-  subs: Array<Watcher>
+  subs: Array<DepTarget>
 
   constructor() {
     this.id = uid++
     this.subs = []
   }
 
-  addSub(sub: Watcher) {
+  addSub(sub: DepTarget) {
     this.subs.push(sub)
   }
 
-  removeSub(sub: Watcher) {
+  removeSub(sub: DepTarget) {
     remove(this.subs, sub)
   }
 
@@ -51,9 +56,9 @@ export default class Dep {
 // This is globally unique because only one watcher
 // can be evaluated at a time.
 Dep.target = null
-const targetStack: Array<Watcher | null | undefined> = []
+const targetStack: Array<DepTarget | null | undefined> = []
 
-export function pushTarget(target?: Watcher | null) {
+export function pushTarget(target?: DepTarget | null) {
   targetStack.push(target)
   Dep.target = target
 }

+ 7 - 6
src/core/observer/index.ts

@@ -5,6 +5,7 @@ import {
   def,
   warn,
   hasOwn,
+  isArray,
   hasProto,
   isObject,
   isPlainObject,
@@ -42,7 +43,7 @@ export class Observer {
     this.dep = new Dep()
     this.vmCount = 0
     def(value, '__ob__', this)
-    if (Array.isArray(value)) {
+    if (isArray(value)) {
       if (hasProto) {
         protoAugment(value, arrayMethods)
       } else {
@@ -115,7 +116,7 @@ export function observe(value: any, asRootData?: boolean): Observer | void {
   } else if (
     shouldObserve &&
     !isServerRendering() &&
-    (Array.isArray(value) || isPlainObject(value)) &&
+    (isArray(value) || isPlainObject(value)) &&
     Object.isExtensible(value) &&
     !value._isVue
   ) {
@@ -161,7 +162,7 @@ export function defineReactive(
         dep.depend()
         if (childOb) {
           childOb.dep.depend()
-          if (Array.isArray(value)) {
+          if (isArray(value)) {
             dependArray(value)
           }
         }
@@ -206,7 +207,7 @@ export function set(
       `Cannot set reactive property on undefined, null, or primitive value: ${target}`
     )
   }
-  if (Array.isArray(target) && isValidArrayIndex(key)) {
+  if (isArray(target) && isValidArrayIndex(key)) {
     target.length = Math.max(target.length, key)
     target.splice(key, 1, val)
     return val
@@ -242,7 +243,7 @@ export function del(target: Array<any> | Object, key: any) {
       `Cannot delete reactive property on undefined, null, or primitive value: ${target}`
     )
   }
-  if (Array.isArray(target) && isValidArrayIndex(key)) {
+  if (isArray(target) && isValidArrayIndex(key)) {
     target.splice(key, 1)
     return
   }
@@ -273,7 +274,7 @@ function dependArray(value: Array<any>) {
   for (let e, i = 0, l = value.length; i < l; i++) {
     e = value[i]
     e && e.__ob__ && e.__ob__.dep.depend()
-    if (Array.isArray(e)) {
+    if (isArray(e)) {
       dependArray(e)
     }
   }

+ 2 - 2
src/core/observer/traverse.ts

@@ -1,4 +1,4 @@
-import { _Set as Set, isObject } from '../util/index'
+import { _Set as Set, isObject, isArray } from '../util/index'
 import type { SimpleSet } from '../util/index'
 import VNode from '../vdom/vnode'
 
@@ -16,7 +16,7 @@ export function traverse(val: any) {
 
 function _traverse(val: any, seen: SimpleSet) {
   let i, keys
-  const isA = Array.isArray(val)
+  const isA = isArray(val)
   if (
     (!isA && !isObject(val)) ||
     Object.isFrozen(val) ||

+ 15 - 9
src/core/observer/watcher.ts

@@ -11,7 +11,7 @@ import {
 
 import { traverse } from './traverse'
 import { queueWatcher } from './scheduler'
-import Dep, { pushTarget, popTarget } from './dep'
+import Dep, { pushTarget, popTarget, DepTarget } from './dep'
 
 import type { SimpleSet } from '../util/index'
 import type { Component } from 'typescript/component'
@@ -23,8 +23,8 @@ let uid = 0
  * and fires callback when the expression value changes.
  * This is used for both the $watch() api and directives.
  */
-export default class Watcher {
-  vm: Component
+export default class Watcher implements DepTarget {
+  vm: Component | null
   expression: string
   cb: Function
   id: number
@@ -39,11 +39,12 @@ export default class Watcher {
   depIds: SimpleSet
   newDepIds: SimpleSet
   before?: Function
+  scheduler?: Function
   getter: Function
   value: any
 
   constructor(
-    vm: Component,
+    vm: Component | null,
     expOrFn: string | Function,
     cb: Function,
     options?: {
@@ -52,14 +53,16 @@ export default class Watcher {
       lazy?: boolean
       sync?: boolean
       before?: Function
+      scheduler?: Function
     } | null,
     isRenderWatcher?: boolean
   ) {
-    this.vm = vm
-    if (isRenderWatcher) {
-      vm._watcher = this
+    if ((this.vm = vm)) {
+      if (isRenderWatcher) {
+        vm._watcher = this
+      }
+      vm._watchers.push(this)
     }
-    vm._watchers.push(this)
     // options
     if (options) {
       this.deep = !!options.deep
@@ -67,6 +70,7 @@ export default class Watcher {
       this.lazy = !!options.lazy
       this.sync = !!options.sync
       this.before = options.before
+      this.scheduler = options.scheduler
     } else {
       this.deep = this.user = this.lazy = this.sync = false
     }
@@ -170,6 +174,8 @@ export default class Watcher {
       this.dirty = true
     } else if (this.sync) {
       this.run()
+    } else if (this.scheduler) {
+      this.scheduler()
     } else {
       queueWatcher(this)
     }
@@ -236,7 +242,7 @@ export default class Watcher {
       // remove self from vm's watcher list
       // this is a somewhat expensive operation so we skip it
       // if the vm is being destroyed.
-      if (!this.vm._isBeingDestroyed) {
+      if (this.vm && !this.vm._isBeingDestroyed) {
         remove(this.vm._watchers, this)
       }
       let i = this.deps.length

+ 3 - 2
src/core/util/debug.ts

@@ -1,5 +1,5 @@
 import config from '../config'
-import { noop } from 'shared/util'
+import { noop, isArray } from 'shared/util'
 import type { Component } from 'typescript/component'
 
 export let warn = noop
@@ -14,6 +14,7 @@ if (__DEV__) {
     str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
 
   warn = (msg, vm) => {
+    // TODO get current instance
     const trace = vm ? generateComponentTrace(vm) : ''
 
     if (config.warnHandler) {
@@ -87,7 +88,7 @@ if (__DEV__) {
           .map(
             (vm, i) =>
               `${i === 0 ? '---> ' : repeat(' ', 5 + i * 2)}${
-                Array.isArray(vm)
+                isArray(vm)
                   ? `${formatComponentName(vm[0])}... (${
                       vm[1]
                     } recursive calls)`

+ 6 - 9
src/core/util/options.ts

@@ -3,6 +3,7 @@ import { warn } from './debug'
 import { set } from '../observer/index'
 import { unicodeRegExp } from './lang'
 import { nativeWatch, hasSymbol } from './env'
+import { isArray } from 'shared/util'
 
 import { ASSET_TYPES, LIFECYCLE_HOOKS } from 'shared/constants'
 
@@ -154,7 +155,7 @@ function mergeHook(
   const res = childVal
     ? parentVal
       ? parentVal.concat(childVal)
-      : Array.isArray(childVal)
+      : isArray(childVal)
       ? childVal
       : [childVal]
     : parentVal
@@ -229,14 +230,10 @@ strats.watch = function (
   for (const key in childVal) {
     let parent = ret[key]
     const child = childVal[key]
-    if (parent && !Array.isArray(parent)) {
+    if (parent && !isArray(parent)) {
       parent = [parent]
     }
-    ret[key] = parent
-      ? parent.concat(child)
-      : Array.isArray(child)
-      ? child
-      : [child]
+    ret[key] = parent ? parent.concat(child) : isArray(child) ? child : [child]
   }
   return ret
 }
@@ -310,7 +307,7 @@ function normalizeProps(options: Record<string, any>, vm?: Component | null) {
   if (!props) return
   const res: Record<string, any> = {}
   let i, val, name
-  if (Array.isArray(props)) {
+  if (isArray(props)) {
     i = props.length
     while (i--) {
       val = props[i]
@@ -344,7 +341,7 @@ function normalizeInject(options: Record<string, any>, vm?: Component | null) {
   const inject = options.inject
   if (!inject) return
   const normalized: Record<string, any> = (options.inject = {})
-  if (Array.isArray(inject)) {
+  if (isArray(inject)) {
     for (let i = 0; i < inject.length; i++) {
       normalized[inject[i]] = { from: inject[i] }
     }

+ 4 - 3
src/core/util/props.ts

@@ -2,6 +2,7 @@ import { warn } from './debug'
 import { observe, toggleObserving, shouldObserve } from '../observer/index'
 import {
   hasOwn,
+  isArray,
   isObject,
   toRawType,
   hyphenate,
@@ -118,7 +119,7 @@ function assertProp(
   let valid = !type || (type as any) === true
   const expectedTypes: string[] = []
   if (type) {
-    if (!Array.isArray(type)) {
+    if (!isArray(type)) {
       type = [type]
     }
     for (let i = 0; i < type.length && !valid; i++) {
@@ -166,7 +167,7 @@ function assertType(
   } else if (expectedType === 'Object') {
     valid = isPlainObject(value)
   } else if (expectedType === 'Array') {
-    valid = Array.isArray(value)
+    valid = isArray(value)
   } else {
     try {
       valid = value instanceof type
@@ -198,7 +199,7 @@ function isSameType(a, b) {
 }
 
 function getTypeIndex(type, expectedTypes): number {
-  if (!Array.isArray(expectedTypes)) {
+  if (!isArray(expectedTypes)) {
     return isSameType(expectedTypes, type) ? 0 : -1
   }
   for (let i = 0, len = expectedTypes.length; i < len; i++) {

+ 2 - 1
src/core/vdom/create-component.ts

@@ -1,4 +1,5 @@
 import VNode from './vnode'
+import { isArray } from 'core/util'
 import { resolveConstructorOptions } from 'core/instance/init'
 import { queueActivatedComponent } from 'core/observer/scheduler'
 import { createFunctionalComponent } from './create-functional-component'
@@ -258,7 +259,7 @@ function transformModel(options, data: any) {
   const callback = data.model.callback
   if (isDef(existing)) {
     if (
-      Array.isArray(existing)
+      isArray(existing)
         ? existing.indexOf(callback) === -1
         : existing !== callback
     ) {

+ 4 - 3
src/core/vdom/create-element.ts

@@ -7,6 +7,7 @@ import {
   warn,
   isDef,
   isUndef,
+  isArray,
   isTrue,
   isObject,
   isPrimitive,
@@ -30,7 +31,7 @@ export function createElement(
   normalizationType: any,
   alwaysNormalize: boolean
 ): VNode | Array<VNode> {
-  if (Array.isArray(data) || isPrimitive(data)) {
+  if (isArray(data) || isPrimitive(data)) {
     normalizationType = children
     children = data
     data = undefined
@@ -75,7 +76,7 @@ export function _createElement(
     )
   }
   // support single function children as default scoped slot
-  if (Array.isArray(children) && typeof children[0] === 'function') {
+  if (isArray(children) && typeof children[0] === 'function') {
     data = data || {}
     data.scopedSlots = { default: children[0] }
     children.length = 0
@@ -126,7 +127,7 @@ export function _createElement(
     // direct component options / constructor
     vnode = createComponent(tag, data, context, children)
   }
-  if (Array.isArray(vnode)) {
+  if (isArray(vnode)) {
     return vnode
   } else if (isDef(vnode)) {
     if (isDef(ns)) applyNS(vnode, ns)

+ 3 - 2
src/core/vdom/create-functional-component.ts

@@ -10,6 +10,7 @@ import {
   isDef,
   isTrue,
   hasOwn,
+  isArray,
   camelize,
   emptyObject,
   validateProp
@@ -78,7 +79,7 @@ export function FunctionalRenderContext(
   if (options._scopeId) {
     this._c = (a, b, c, d) => {
       const vnode = createElement(contextVm, a, b, c, d, needNormalization)
-      if (vnode && !Array.isArray(vnode)) {
+      if (vnode && !isArray(vnode)) {
         vnode.fnScopeId = options._scopeId
         vnode.fnContext = parent
       }
@@ -129,7 +130,7 @@ export function createFunctionalComponent(
       options,
       renderContext
     )
-  } else if (Array.isArray(vnode)) {
+  } else if (isArray(vnode)) {
     const vnodes = normalizeChildren(vnode) || []
     const res = new Array(vnodes.length)
     for (let i = 0; i < vnodes.length; i++) {

+ 2 - 2
src/core/vdom/helpers/get-first-component-child.ts

@@ -1,11 +1,11 @@
-import { isDef } from 'shared/util'
+import { isDef, isArray } from 'shared/util'
 import VNode from '../vnode'
 import { isAsyncPlaceholder } from './is-async-placeholder'
 
 export function getFirstComponentChild(
   children?: Array<VNode>
 ): VNode | undefined {
-  if (Array.isArray(children)) {
+  if (isArray(children)) {
     for (let i = 0; i < children.length; i++) {
       const c = children[i]
       if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {

+ 11 - 4
src/core/vdom/helpers/normalize-children.ts

@@ -1,5 +1,12 @@
 import VNode, { createTextVNode } from 'core/vdom/vnode'
-import { isFalse, isTrue, isDef, isUndef, isPrimitive } from 'shared/util'
+import {
+  isFalse,
+  isTrue,
+  isArray,
+  isDef,
+  isUndef,
+  isPrimitive
+} from 'shared/util'
 
 // The template compiler attempts to minimize the need for normalization by
 // statically analyzing the template at compile time.
@@ -15,7 +22,7 @@ import { isFalse, isTrue, isDef, isUndef, isPrimitive } from 'shared/util'
 // because functional components already normalize their own children.
 export function simpleNormalizeChildren(children: any) {
   for (let i = 0; i < children.length; i++) {
-    if (Array.isArray(children[i])) {
+    if (isArray(children[i])) {
       return Array.prototype.concat.apply([], children)
     }
   }
@@ -29,7 +36,7 @@ export function simpleNormalizeChildren(children: any) {
 export function normalizeChildren(children: any): Array<VNode> | undefined {
   return isPrimitive(children)
     ? [createTextVNode(children)]
-    : Array.isArray(children)
+    : isArray(children)
     ? normalizeArrayChildren(children)
     : undefined
 }
@@ -50,7 +57,7 @@ function normalizeArrayChildren(
     lastIndex = res.length - 1
     last = res[lastIndex]
     //  nested
-    if (Array.isArray(c)) {
+    if (isArray(c)) {
       if (c.length > 0) {
         c = normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`)
         // merge adjacent text nodes

+ 2 - 2
src/core/vdom/helpers/normalize-scoped-slots.ts

@@ -1,6 +1,6 @@
 import { def } from 'core/util/lang'
 import { normalizeChildren } from 'core/vdom/helpers/normalize-children'
-import { emptyObject } from 'shared/util'
+import { emptyObject, isArray } from 'shared/util'
 import { isAsyncPlaceholder } from './is-async-placeholder'
 import type VNode from '../vnode'
 
@@ -58,7 +58,7 @@ function normalizeScopedSlot(normalSlots, key, fn) {
   const normalized = function () {
     let res = arguments.length ? fn.apply(null, arguments) : fn({})
     res =
-      res && typeof res === 'object' && !Array.isArray(res)
+      res && typeof res === 'object' && !isArray(res)
         ? [res] // single vnode
         : normalizeChildren(res)
     const vnode: VNode | null = res && res[0]

+ 2 - 2
src/core/vdom/helpers/update-listeners.ts

@@ -1,5 +1,5 @@
 import { warn, invokeWithErrorHandling } from 'core/util/index'
-import { cached, isUndef, isTrue } from 'shared/util'
+import { cached, isUndef, isTrue, isArray } from 'shared/util'
 import type { Component } from 'typescript/component'
 
 const normalizeEvent = cached(
@@ -34,7 +34,7 @@ export function createFnInvoker(
 ): Function {
   function invoker() {
     const fns = invoker.fns
-    if (Array.isArray(fns)) {
+    if (isArray(fns)) {
       const cloned = fns.slice()
       for (let i = 0; i < cloned.length; i++) {
         invokeWithErrorHandling(

+ 3 - 3
src/core/vdom/modules/ref.ts

@@ -1,4 +1,4 @@
-import { remove, isDef } from 'shared/util'
+import { remove, isDef, isArray } from 'shared/util'
 import type { VNodeWithData } from 'typescript/vnode'
 
 export default {
@@ -25,14 +25,14 @@ export function registerRef(vnode: VNodeWithData, isRemoval?: boolean) {
   const refs = vm.$refs
   const obj = refs[key]
   if (isRemoval) {
-    if (Array.isArray(obj)) {
+    if (isArray(obj)) {
       remove(obj, ref)
     } else if (obj === ref) {
       refs[key] = undefined
     }
   } else {
     if (vnode.data.refInFor) {
-      if (!Array.isArray(obj)) {
+      if (!isArray(obj)) {
         refs[key] = [ref]
       } else if (obj.indexOf(ref) < 0) {
         obj.push(ref)

+ 2 - 1
src/core/vdom/patch.ts

@@ -23,6 +23,7 @@ import {
   isDef,
   isUndef,
   isTrue,
+  isArray,
   makeMap,
   isRegExp,
   isPrimitive
@@ -261,7 +262,7 @@ export function createPatchFunction(backend) {
   }
 
   function createChildren(vnode, children, insertedVnodeQueue) {
-    if (Array.isArray(children)) {
+    if (isArray(children)) {
       if (__DEV__) {
         checkDuplicateKeys(children)
       }

+ 2 - 0
src/shared/util.ts

@@ -1,5 +1,7 @@
 export const emptyObject: Record<string, any> = Object.freeze({})
 
+export const isArray = Array.isArray
+
 // These helpers produce better VM code in JS engines due to their
 // explicitness and function inlining.
 export function isUndef(v: any): v is undefined | null {