types: improve public component type

This commit is contained in:
Evan You 2018-10-13 21:13:56 -04:00
parent b3e3fd6ef5
commit a9b608266e
6 changed files with 102 additions and 71 deletions

View File

@ -1,4 +1,4 @@
import { EMPTY_OBJ } from './utils' import { EMPTY_OBJ, NOOP } from './utils'
import { VNode, Slots, RenderNode, MountedVNode } from './vdom' import { VNode, Slots, RenderNode, MountedVNode } from './vdom'
import { import {
Data, Data,
@ -13,37 +13,43 @@ import { nextTick } from '@vue/scheduler'
import { ErrorTypes } from './errorHandling' import { ErrorTypes } from './errorHandling'
import { initializeComponentInstance } from './componentUtils' import { initializeComponentInstance } from './componentUtils'
export interface ComponentClass extends ComponentClassOptions { // public component instance type
options?: ComponentOptions export interface Component<P = {}, D = {}> extends PublicInstanceMethods {
new <P = {}, D = {}>(): MergedComponent<P, D> readonly $el: any
readonly $vnode: MountedVNode
readonly $parentVNode: MountedVNode
readonly $data: D
readonly $props: Readonly<P>
readonly $attrs: Readonly<Data>
readonly $slots: Slots
readonly $root: Component
readonly $parent: Component
readonly $children: Component[]
readonly $options: ComponentOptions<P, D, this>
readonly $refs: Record<string | symbol, any>
readonly $proxy: this
} }
export type MergedComponent<P, D> = D & P & ComponentInstance<P, D> interface PublicInstanceMethods {
$forceUpdate(): void
export interface FunctionalComponent<P = {}> { $nextTick(fn: () => any): Promise<void>
(props: P, slots: Slots, attrs: Data): any $watch(
pure?: boolean keyOrFn: string | ((this: this) => any),
props?: ComponentPropsOptions<P> cb: (this: this, newValue: any, oldValue: any) => void,
displayName?: string options?: WatchOptions
): () => void
$on(event: string, fn: Function): this
$once(event: string, fn: Function): this
$off(event?: string, fn?: Function): this
$emit(name: string, ...payload: any[]): this
} }
export type ComponentType = ComponentClass | FunctionalComponent interface APIMethods<P, D> {
export interface ComponentInstance<P = {}, D = {}> extends InternalComponent {
constructor: ComponentClass
$vnode: MountedVNode
$data: D
$props: Readonly<P>
$attrs: Data
$slots: Slots
$root: ComponentInstance
$children: ComponentInstance[]
data?(): Partial<D> data?(): Partial<D>
render(props: Readonly<P>, slots: Slots, attrs: Data): any render(props: Readonly<P>, slots: Slots, attrs: Data): any
renderTracked?(e: DebuggerEvent): void }
renderTriggered?(e: DebuggerEvent): void
interface LifecycleMethods {
beforeCreate?(): void beforeCreate?(): void
created?(): void created?(): void
beforeMount?(): void beforeMount?(): void
@ -60,50 +66,83 @@ export interface ComponentInstance<P = {}, D = {}> extends InternalComponent {
) => boolean | void ) => boolean | void
activated?(): void activated?(): void
deactivated?(): void deactivated?(): void
renderTracked?(e: DebuggerEvent): void
renderTriggered?(e: DebuggerEvent): void
}
export interface ComponentClass extends ComponentClassOptions {
options?: ComponentOptions
new <P = {}, D = {}>(): Component<P, D> & D & P
}
export interface FunctionalComponent<P = {}> {
(props: P, slots: Slots, attrs: Data): any
pure?: boolean
props?: ComponentPropsOptions<P>
displayName?: string
}
export type ComponentType = ComponentClass | FunctionalComponent
// Internal type that represents a mounted instance.
// It extends InternalComponent with mounted instance properties.
export interface ComponentInstance<P = {}, D = {}>
extends InternalComponent,
APIMethods<P, D>,
LifecycleMethods {
constructor: ComponentClass
$vnode: MountedVNode
$data: D
$props: P
$attrs: Data
$slots: Slots
$root: ComponentInstance
$children: ComponentInstance[]
_updateHandle: Autorun _updateHandle: Autorun
_queueJob: ((fn: () => void) => void) _queueJob: ((fn: () => void) => void)
$forceUpdate: () => void
$nextTick: (fn: () => void) => Promise<any>
_self: ComponentInstance<D, P> // on proxies only _self: ComponentInstance<D, P> // on proxies only
} }
class InternalComponent { // actual implementation of the component
public get $el(): RenderNode | null { class InternalComponent implements PublicInstanceMethods {
get $el(): any {
return this.$vnode && this.$vnode.el return this.$vnode && this.$vnode.el
} }
public $vnode: VNode | null = null $vnode: VNode | null = null
public $parentVNode: VNode | null = null $parentVNode: VNode | null = null
public $data: Data | null = null $data: Data | null = null
public $props: Data | null = null $props: Data | null = null
public $attrs: Data | null = null $attrs: Data | null = null
public $slots: Slots | null = null $slots: Slots | null = null
public $root: ComponentInstance | null = null $root: ComponentInstance | null = null
public $parent: ComponentInstance | null = null $parent: ComponentInstance | null = null
public $children: ComponentInstance[] = [] $children: ComponentInstance[] = []
public $options: ComponentOptions $options: ComponentOptions
public $refs: Record<string, ComponentInstance | RenderNode> = {} $refs: Record<string, ComponentInstance | RenderNode> = {}
public $proxy: any = null $proxy: any = null
public $forceUpdate: (() => void) | null = null
public _rawData: Data | null = null _rawData: Data | null = null
public _computedGetters: Record<string, ComputedGetter> | null = null _computedGetters: Record<string, ComputedGetter> | null = null
public _watchHandles: Set<Autorun> | null = null _watchHandles: Set<Autorun> | null = null
public _mounted: boolean = false _mounted: boolean = false
public _unmounted: boolean = false _unmounted: boolean = false
public _events: { [event: string]: Function[] | null } | null = null _events: { [event: string]: Function[] | null } | null = null
public _updateHandle: Autorun | null = null _updateHandle: Autorun | null = null
public _queueJob: ((fn: () => void) => void) | null = null _queueJob: ((fn: () => void) => void) | null = null
public _isVue: boolean = true _isVue: boolean = true
public _inactiveRoot: boolean = false _inactiveRoot: boolean = false
constructor() { constructor() {
initializeComponentInstance(this as any) initializeComponentInstance(this as any)
} }
$nextTick(fn: () => any): Promise<any> { // to be set by renderer during mount
$forceUpdate: () => void = NOOP
$nextTick(fn: () => any): Promise<void> {
return nextTick(fn) return nextTick(fn)
} }

View File

@ -1,4 +1,4 @@
import { ComponentInstance, MergedComponent } from './component' import { ComponentInstance, Component } from './component'
import { Slots } from './vdom' import { Slots } from './vdom'
export type Data = Record<string, any> export type Data = Record<string, any>
@ -10,7 +10,7 @@ export interface ComponentClassOptions<P = {}, This = ComponentInstance> {
displayName?: string displayName?: string
} }
export interface ComponentOptions<P = {}, D = {}, This = MergedComponent<P, D>> export interface ComponentOptions<P = {}, D = {}, This = Component<P, D>>
extends ComponentClassOptions<P, This> { extends ComponentClassOptions<P, This> {
data?(): D data?(): D
render?: (this: This, props: Readonly<Data>, slots: Slots, attrs: Data) => any render?: (this: This, props: Readonly<Data>, slots: Slots, attrs: Data) => any

View File

@ -39,7 +39,7 @@ export function createComponentInstance(
// always pass args in super() // always pass args in super()
currentVNode = vnode currentVNode = vnode
currentContextVNode = contextVNode currentContextVNode = contextVNode
const instance = (vnode.children = new Component()) as ComponentInstance const instance = (vnode.children = new Component() as ComponentInstance)
// then we finish the initialization by collecting properties set on the // then we finish the initialization by collecting properties set on the
// instance // instance
initializeState(instance) initializeState(instance)

View File

@ -1,9 +1,5 @@
import { ChildrenFlags } from './flags' import { ChildrenFlags } from './flags'
import { import { ComponentClass, FunctionalComponent, Component } from './component'
ComponentClass,
FunctionalComponent,
ComponentInstance
} from './component'
import { ComponentOptions } from './componentOptions' import { ComponentOptions } from './componentOptions'
import { import {
VNode, VNode,
@ -93,9 +89,9 @@ interface createElement extends VNodeFactories {
children?: RawChildrenType | RawSlots children?: RawChildrenType | RawSlots
): VNode ): VNode
// class // class
<P>(tag: new () => ComponentInstance<P>, children?: RawChildrenType): VNode <P>(tag: new () => Component<P>, children?: RawChildrenType): VNode
<P>( <P>(
tag: new () => ComponentInstance<P>, tag: new () => Component<P>,
data?: (P & BuiltInProps & Differ) | null, data?: (P & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots children?: RawChildrenType | RawSlots
): VNode ): VNode

View File

@ -24,12 +24,7 @@ export { createAsyncComponent } from './optional/asyncComponent'
export { KeepAlive } from './optional/keepAlive' export { KeepAlive } from './optional/keepAlive'
// flags & types // flags & types
export { export { ComponentType, ComponentClass, FunctionalComponent } from './component'
ComponentType,
ComponentClass,
FunctionalComponent,
ComponentInstance
} from './component'
export * from './componentOptions' export * from './componentOptions'
export { VNodeFlags, ChildrenFlags } from './flags' export { VNodeFlags, ChildrenFlags } from './flags'
export { VNode, Slots } from './vdom' export { VNode, Slots } from './vdom'

View File

@ -56,6 +56,7 @@ export type VNodeChildren =
| ComponentInstance // COMPONENT_STATEFUL | ComponentInstance // COMPONENT_STATEFUL
| VNode // COMPONENT_FUNCTIONAL | VNode // COMPONENT_FUNCTIONAL
| string // TEXT | string // TEXT
| null
export type Key = string | number export type Key = string | number