refactor(types): widen Component type to include consutructor types

returned from `defineComponent`

ref: https://github.com/vuejs/vue-router-next/pull/421
also close #1880

Previous `Component` type is now exported as `ConcreteComponent`.

This introduces a minor breaking change when calling `h(comp, { ... })`
will now fail if `comp` is a of generic `Component` type, since it does
not specify what props it expects.
This commit is contained in:
Evan You 2020-08-19 16:11:29 -04:00
parent 4baf852a34
commit eb2ae44d94
21 changed files with 117 additions and 102 deletions

View File

@ -11,8 +11,7 @@ import {
watch, watch,
watchEffect, watchEffect,
onUnmounted, onUnmounted,
onErrorCaptured, onErrorCaptured
Component
} from '@vue/runtime-test' } from '@vue/runtime-test'
describe('Suspense', () => { describe('Suspense', () => {
@ -31,7 +30,7 @@ describe('Suspense', () => {
setup(props: any, { slots }: any) { setup(props: any, { slots }: any) {
const p = new Promise(resolve => { const p = new Promise(resolve => {
setTimeout(() => { setTimeout(() => {
resolve(() => h<Component>(comp, props, slots)) resolve(() => h(comp, props, slots))
}, delay) }, delay)
}) })
// in Node 12, due to timer/nextTick mechanism change, we have to wait // in Node 12, due to timer/nextTick mechanism change, we have to wait

View File

@ -1,21 +1,19 @@
import { import {
PublicAPIComponent,
Component, Component,
ConcreteComponent,
currentInstance, currentInstance,
ComponentInternalInstance, ComponentInternalInstance,
isInSSRComponentSetup isInSSRComponentSetup
} from './component' } from './component'
import { isFunction, isObject } from '@vue/shared' import { isFunction, isObject } from '@vue/shared'
import { ComponentPublicInstance } from './componentProxy' import { ComponentPublicInstance } from './componentPublicInstance'
import { createVNode } from './vnode' import { createVNode } from './vnode'
import { defineComponent } from './apiDefineComponent' import { defineComponent } from './apiDefineComponent'
import { warn } from './warning' import { warn } from './warning'
import { ref } from '@vue/reactivity' import { ref } from '@vue/reactivity'
import { handleError, ErrorCodes } from './errorHandling' import { handleError, ErrorCodes } from './errorHandling'
export type AsyncComponentResolveResult<T = PublicAPIComponent> = export type AsyncComponentResolveResult<T = Component> = T | { default: T } // es modules
| T
| { default: T } // es modules
export type AsyncComponentLoader<T = any> = () => Promise< export type AsyncComponentLoader<T = any> = () => Promise<
AsyncComponentResolveResult<T> AsyncComponentResolveResult<T>
@ -23,8 +21,8 @@ export type AsyncComponentLoader<T = any> = () => Promise<
export interface AsyncComponentOptions<T = any> { export interface AsyncComponentOptions<T = any> {
loader: AsyncComponentLoader<T> loader: AsyncComponentLoader<T>
loadingComponent?: PublicAPIComponent loadingComponent?: Component
errorComponent?: PublicAPIComponent errorComponent?: Component
delay?: number delay?: number
timeout?: number timeout?: number
suspensible?: boolean suspensible?: boolean
@ -37,7 +35,7 @@ export interface AsyncComponentOptions<T = any> {
} }
export function defineAsyncComponent< export function defineAsyncComponent<
T extends PublicAPIComponent = { new (): ComponentPublicInstance } T extends Component = { new (): ComponentPublicInstance }
>(source: AsyncComponentLoader<T> | AsyncComponentOptions<T>): T { >(source: AsyncComponentLoader<T> | AsyncComponentOptions<T>): T {
if (isFunction(source)) { if (isFunction(source)) {
source = { loader: source } source = { loader: source }
@ -53,8 +51,8 @@ export function defineAsyncComponent<
onError: userOnError onError: userOnError
} = source } = source
let pendingRequest: Promise<Component> | null = null let pendingRequest: Promise<ConcreteComponent> | null = null
let resolvedComp: Component | undefined let resolvedComp: ConcreteComponent | undefined
let retries = 0 let retries = 0
const retry = () => { const retry = () => {
@ -63,8 +61,8 @@ export function defineAsyncComponent<
return load() return load()
} }
const load = (): Promise<Component> => { const load = (): Promise<ConcreteComponent> => {
let thisRequest: Promise<Component> let thisRequest: Promise<ConcreteComponent>
return ( return (
pendingRequest || pendingRequest ||
(thisRequest = pendingRequest = loader() (thisRequest = pendingRequest = loader()
@ -135,7 +133,9 @@ export function defineAsyncComponent<
onError(err) onError(err)
return () => return () =>
errorComponent errorComponent
? createVNode(errorComponent as Component, { error: err }) ? createVNode(errorComponent as ConcreteComponent, {
error: err
})
: null : null
}) })
} }
@ -175,11 +175,11 @@ export function defineAsyncComponent<
if (loaded.value && resolvedComp) { if (loaded.value && resolvedComp) {
return createInnerComp(resolvedComp, instance) return createInnerComp(resolvedComp, instance)
} else if (error.value && errorComponent) { } else if (error.value && errorComponent) {
return createVNode(errorComponent as Component, { return createVNode(errorComponent as ConcreteComponent, {
error: error.value error: error.value
}) })
} else if (loadingComponent && !delayed.value) { } else if (loadingComponent && !delayed.value) {
return createVNode(loadingComponent as Component) return createVNode(loadingComponent as ConcreteComponent)
} }
} }
} }
@ -187,7 +187,7 @@ export function defineAsyncComponent<
} }
function createInnerComp( function createInnerComp(
comp: Component, comp: ConcreteComponent,
{ vnode: { props, children } }: ComponentInternalInstance { vnode: { props, children } }: ComponentInternalInstance
) { ) {
return createVNode(comp, props, children) return createVNode(comp, props, children)

View File

@ -1,11 +1,11 @@
import { import {
Component, ConcreteComponent,
Data, Data,
validateComponentName, validateComponentName,
PublicAPIComponent Component
} from './component' } from './component'
import { ComponentOptions } from './componentOptions' import { ComponentOptions } from './componentOptions'
import { ComponentPublicInstance } from './componentProxy' import { ComponentPublicInstance } from './componentPublicInstance'
import { Directive, validateDirectiveName } from './directives' import { Directive, validateDirectiveName } from './directives'
import { RootRenderFunction } from './renderer' import { RootRenderFunction } from './renderer'
import { InjectionKey } from './apiInject' import { InjectionKey } from './apiInject'
@ -21,8 +21,8 @@ export interface App<HostElement = any> {
config: AppConfig config: AppConfig
use(plugin: Plugin, ...options: any[]): this use(plugin: Plugin, ...options: any[]): this
mixin(mixin: ComponentOptions): this mixin(mixin: ComponentOptions): this
component(name: string): PublicAPIComponent | undefined component(name: string): Component | undefined
component(name: string, component: PublicAPIComponent): this component(name: string, component: Component): this
directive(name: string): Directive | undefined directive(name: string): Directive | undefined
directive(name: string, directive: Directive): this directive(name: string, directive: Directive): this
mount( mount(
@ -33,7 +33,7 @@ export interface App<HostElement = any> {
provide<T>(key: InjectionKey<T> | string, value: T): this provide<T>(key: InjectionKey<T> | string, value: T): this
// internal, but we need to expose these for the server-renderer and devtools // internal, but we need to expose these for the server-renderer and devtools
_component: Component _component: ConcreteComponent
_props: Data | null _props: Data | null
_container: HostElement | null _container: HostElement | null
_context: AppContext _context: AppContext
@ -70,7 +70,7 @@ export interface AppContext {
app: App // for devtools app: App // for devtools
config: AppConfig config: AppConfig
mixins: ComponentOptions[] mixins: ComponentOptions[]
components: Record<string, PublicAPIComponent> components: Record<string, Component>
directives: Record<string, Directive> directives: Record<string, Directive>
provides: Record<string | symbol, any> provides: Record<string | symbol, any>
reload?: () => void // HMR only reload?: () => void // HMR only
@ -104,7 +104,7 @@ export function createAppContext(): AppContext {
} }
export type CreateAppFunction<HostElement> = ( export type CreateAppFunction<HostElement> = (
rootComponent: PublicAPIComponent, rootComponent: Component,
rootProps?: Data | null rootProps?: Data | null
) => App<HostElement> ) => App<HostElement>
@ -124,7 +124,7 @@ export function createAppAPI<HostElement>(
let isMounted = false let isMounted = false
const app: App = (context.app = { const app: App = (context.app = {
_component: rootComponent as Component, _component: rootComponent as ConcreteComponent,
_props: rootProps, _props: rootProps,
_container: null, _container: null,
_context: context, _context: context,
@ -177,7 +177,7 @@ export function createAppAPI<HostElement>(
return app return app
}, },
component(name: string, component?: PublicAPIComponent): any { component(name: string, component?: Component): any {
if (__DEV__) { if (__DEV__) {
validateComponentName(name, context.config) validateComponentName(name, context.config)
} }
@ -208,7 +208,10 @@ export function createAppAPI<HostElement>(
mount(rootContainer: HostElement, isHydrate?: boolean): any { mount(rootContainer: HostElement, isHydrate?: boolean): any {
if (!isMounted) { if (!isMounted) {
const vnode = createVNode(rootComponent as Component, rootProps) const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
// store app context on the root VNode. // store app context on the root VNode.
// this will be set on the root instance on initial mount. // this will be set on the root instance on initial mount.
vnode.appContext = context vnode.appContext = context

View File

@ -16,7 +16,7 @@ import {
import { import {
CreateComponentPublicInstance, CreateComponentPublicInstance,
ComponentPublicInstanceConstructor ComponentPublicInstanceConstructor
} from './componentProxy' } from './componentPublicInstance'
import { ExtractPropTypes, ComponentPropsOptions } from './componentProps' import { ExtractPropTypes, ComponentPropsOptions } from './componentProps'
import { EmitsOptions } from './componentEmits' import { EmitsOptions } from './componentEmits'
import { isFunction } from '@vue/shared' import { isFunction } from '@vue/shared'
@ -205,7 +205,5 @@ export function defineComponent<
// implementation, close to no-op // implementation, close to no-op
export function defineComponent(options: unknown) { export function defineComponent(options: unknown) {
return isFunction(options) return isFunction(options) ? { setup: options, name: options.name } : options
? { setup: options, name: options.name }
: options
} }

View File

@ -5,7 +5,7 @@ import {
setCurrentInstance, setCurrentInstance,
isInSSRComponentSetup isInSSRComponentSetup
} from './component' } from './component'
import { ComponentPublicInstance } from './componentProxy' import { ComponentPublicInstance } from './componentPublicInstance'
import { callWithAsyncErrorHandling, ErrorTypeStrings } from './errorHandling' import { callWithAsyncErrorHandling, ErrorTypeStrings } from './errorHandling'
import { warn } from './warning' import { warn } from './warning'
import { capitalize } from '@vue/shared' import { capitalize } from '@vue/shared'

View File

@ -7,14 +7,14 @@ import {
proxyRefs proxyRefs
} from '@vue/reactivity' } from '@vue/reactivity'
import { import {
CreateComponentPublicInstance,
ComponentPublicInstance, ComponentPublicInstance,
PublicInstanceProxyHandlers, PublicInstanceProxyHandlers,
RuntimeCompiledPublicInstanceProxyHandlers, RuntimeCompiledPublicInstanceProxyHandlers,
createRenderContext, createRenderContext,
exposePropsOnRenderContext, exposePropsOnRenderContext,
exposeSetupStateOnRenderContext exposeSetupStateOnRenderContext,
} from './componentProxy' ComponentPublicInstanceConstructor
} from './componentPublicInstance'
import { import {
ComponentPropsOptions, ComponentPropsOptions,
NormalizedPropsOptions, NormalizedPropsOptions,
@ -110,21 +110,19 @@ export interface ClassComponent {
__vccOpts: ComponentOptions __vccOpts: ComponentOptions
} }
export type Component = ComponentOptions | FunctionalComponent<any, any> /**
* Concrete component type matches its actual value: it's either an options
* object, or a function. Use this where the code expects to work with actual
* values, e.g. checking if its a function or not. This is mostly for internal
* implementation code.
*/
export type ConcreteComponent = ComponentOptions | FunctionalComponent<any, any>
// A type used in public APIs where a component type is expected. /**
// The constructor type is an artificial type returned by defineComponent(). * A type used in public APIs where a component type is expected.
export type PublicAPIComponent = * The constructor type is an artificial type returned by defineComponent().
| Component */
| { export type Component = ConcreteComponent | ComponentPublicInstanceConstructor
new (...args: any[]): CreateComponentPublicInstance<
any,
any,
any,
any,
any
>
}
export { ComponentOptions } export { ComponentOptions }
@ -174,7 +172,7 @@ export type InternalRenderFunction = {
*/ */
export interface ComponentInternalInstance { export interface ComponentInternalInstance {
uid: number uid: number
type: Component type: ConcreteComponent
parent: ComponentInternalInstance | null parent: ComponentInternalInstance | null
root: ComponentInternalInstance root: ComponentInternalInstance
appContext: AppContext appContext: AppContext
@ -346,7 +344,7 @@ export function createComponentInstance(
parent: ComponentInternalInstance | null, parent: ComponentInternalInstance | null,
suspense: SuspenseBoundary | null suspense: SuspenseBoundary | null
) { ) {
const type = vnode.type as Component const type = vnode.type as ConcreteComponent
// inherit parent app context - or - if root, adopt from root vnode // inherit parent app context - or - if root, adopt from root vnode
const appContext = const appContext =
(parent ? parent.appContext : vnode.appContext) || emptyAppContext (parent ? parent.appContext : vnode.appContext) || emptyAppContext
@ -703,7 +701,7 @@ const classify = (str: string): string =>
/* istanbul ignore next */ /* istanbul ignore next */
export function formatComponentName( export function formatComponentName(
instance: ComponentInternalInstance | null, instance: ComponentInternalInstance | null,
Component: Component, Component: ConcreteComponent,
isRoot = false isRoot = false
): string { ): string {
let name = isFunction(Component) let name = isFunction(Component)

View File

@ -8,7 +8,7 @@ import {
isFunction, isFunction,
extend extend
} from '@vue/shared' } from '@vue/shared'
import { ComponentInternalInstance, Component } from './component' import { ComponentInternalInstance, ConcreteComponent } from './component'
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling' import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
import { warn } from './warning' import { warn } from './warning'
import { normalizePropsOptions } from './componentProps' import { normalizePropsOptions } from './componentProps'
@ -94,7 +94,7 @@ export function emit(
} }
function normalizeEmitsOptions( function normalizeEmitsOptions(
comp: Component comp: ConcreteComponent
): ObjectEmitsOptions | undefined { ): ObjectEmitsOptions | undefined {
if (hasOwn(comp, '__emits')) { if (hasOwn(comp, '__emits')) {
return comp.__emits return comp.__emits
@ -131,7 +131,7 @@ function normalizeEmitsOptions(
// Check if an incoming prop key is a declared emit event listener. // Check if an incoming prop key is a declared emit event listener.
// e.g. With `emits: { click: null }`, props named `onClick` and `onclick` are // e.g. With `emits: { click: null }`, props named `onClick` and `onclick` are
// both considered matched listeners. // both considered matched listeners.
export function isEmitListener(comp: Component, key: string): boolean { export function isEmitListener(comp: ConcreteComponent, key: string): boolean {
let emits: ObjectEmitsOptions | undefined let emits: ObjectEmitsOptions | undefined
if (!isOn(key) || !(emits = normalizeEmitsOptions(comp))) { if (!isOn(key) || !(emits = normalizeEmitsOptions(comp))) {
return false return false

View File

@ -3,8 +3,8 @@ import {
Data, Data,
SetupContext, SetupContext,
ComponentInternalOptions, ComponentInternalOptions,
PublicAPIComponent,
Component, Component,
ConcreteComponent,
InternalRenderFunction InternalRenderFunction
} from './component' } from './component'
import { import {
@ -52,7 +52,7 @@ import { Directive } from './directives'
import { import {
CreateComponentPublicInstance, CreateComponentPublicInstance,
ComponentPublicInstance ComponentPublicInstance
} from './componentProxy' } from './componentPublicInstance'
import { warn } from './warning' import { warn } from './warning'
import { VNodeChild } from './vnode' import { VNodeChild } from './vnode'
@ -103,7 +103,7 @@ export interface ComponentOptionsBase<
// Luckily `render()` doesn't need any arguments nor does it care about return // Luckily `render()` doesn't need any arguments nor does it care about return
// type. // type.
render?: Function render?: Function
components?: Record<string, PublicAPIComponent> components?: Record<string, Component>
directives?: Record<string, Directive> directives?: Record<string, Directive>
inheritAttrs?: boolean inheritAttrs?: boolean
emits?: (E | EE[]) & ThisType<void> emits?: (E | EE[]) & ThisType<void>
@ -132,7 +132,7 @@ export interface ComponentOptionsBase<
* marker for AsyncComponentWrapper * marker for AsyncComponentWrapper
* @internal * @internal
*/ */
__asyncLoader?: () => Promise<Component> __asyncLoader?: () => Promise<ConcreteComponent>
/** /**
* cache for merged $options * cache for merged $options
* @internal * @internal

View File

@ -27,7 +27,7 @@ import {
Data, Data,
ComponentInternalInstance, ComponentInternalInstance,
ComponentOptions, ComponentOptions,
Component ConcreteComponent
} from './component' } from './component'
import { isEmitListener } from './componentEmits' import { isEmitListener } from './componentEmits'
import { InternalObjectKey } from './vnode' import { InternalObjectKey } from './vnode'
@ -310,7 +310,7 @@ function resolvePropValue(
} }
export function normalizePropsOptions( export function normalizePropsOptions(
comp: Component comp: ConcreteComponent
): NormalizedPropsOptions | [] { ): NormalizedPropsOptions | [] {
if (comp.__props) { if (comp.__props) {
return comp.__props return comp.__props
@ -411,7 +411,7 @@ function getTypeIndex(
/** /**
* dev only * dev only
*/ */
function validateProps(props: Data, comp: Component) { function validateProps(props: Data, comp: ConcreteComponent) {
const rawValues = toRaw(props) const rawValues = toRaw(props)
const options = normalizePropsOptions(comp)[0] const options = normalizePropsOptions(comp)[0]
for (const key in options) { for (const key in options) {

View File

@ -101,6 +101,15 @@ type UnwrapMixinsType<
type EnsureNonVoid<T> = T extends void ? {} : T type EnsureNonVoid<T> = T extends void ? {} : T
export type ComponentPublicInstanceConstructor<
T extends ComponentPublicInstance = ComponentPublicInstance<any>
> = {
__isFragment?: never
__isTeleport?: never
__isSuspense?: never
new (): T
}
export type CreateComponentPublicInstance< export type CreateComponentPublicInstance<
P = {}, P = {},
B = {}, B = {},
@ -162,12 +171,6 @@ export type ComponentPublicInstance<
M & M &
ComponentCustomProperties ComponentCustomProperties
export type ComponentPublicInstanceConstructor<
T extends ComponentPublicInstance
> = {
new (): T
}
type PublicPropertiesMap = Record<string, (i: ComponentInternalInstance) => any> type PublicPropertiesMap = Record<string, (i: ComponentInternalInstance) => any>
const publicPropertiesMap: PublicPropertiesMap = extend(Object.create(null), { const publicPropertiesMap: PublicPropertiesMap = extend(Object.create(null), {

View File

@ -1,5 +1,5 @@
import { import {
Component, ConcreteComponent,
getCurrentInstance, getCurrentInstance,
FunctionalComponent, FunctionalComponent,
SetupContext, SetupContext,
@ -33,7 +33,7 @@ import {
invokeVNodeHook invokeVNodeHook
} from '../renderer' } from '../renderer'
import { setTransitionHooks } from './BaseTransition' import { setTransitionHooks } from './BaseTransition'
import { ComponentRenderContext } from '../componentProxy' import { ComponentRenderContext } from '../componentPublicInstance'
type MatchPattern = string | RegExp | string[] | RegExp[] type MatchPattern = string | RegExp | string[] | RegExp[]
@ -43,7 +43,7 @@ export interface KeepAliveProps {
max?: number | string max?: number | string
} }
type CacheKey = string | number | Component type CacheKey = string | number | ConcreteComponent
type Cache = Map<CacheKey, VNode> type Cache = Map<CacheKey, VNode>
type Keys = Set<CacheKey> type Keys = Set<CacheKey>
@ -151,7 +151,7 @@ const KeepAliveImpl = {
function pruneCache(filter?: (name: string) => boolean) { function pruneCache(filter?: (name: string) => boolean) {
cache.forEach((vnode, key) => { cache.forEach((vnode, key) => {
const name = getName(vnode.type as Component) const name = getName(vnode.type as ConcreteComponent)
if (name && (!filter || !filter(name))) { if (name && (!filter || !filter(name))) {
pruneCacheEntry(key) pruneCacheEntry(key)
} }
@ -228,7 +228,7 @@ const KeepAliveImpl = {
return vnode return vnode
} }
const comp = vnode.type as Component const comp = vnode.type as ConcreteComponent
const name = getName(comp) const name = getName(comp)
const { include, exclude, max } = props const { include, exclude, max } = props
@ -291,7 +291,7 @@ export const KeepAlive = (KeepAliveImpl as any) as {
} }
} }
function getName(comp: Component): string | void { function getName(comp: ConcreteComponent): string | void {
return (comp as FunctionalComponent).displayName || comp.name return (comp as FunctionalComponent).displayName || comp.name
} }

View File

@ -17,7 +17,7 @@ import { warn } from './warning'
import { ComponentInternalInstance, Data } from './component' import { ComponentInternalInstance, Data } from './component'
import { currentRenderingInstance } from './componentRenderUtils' import { currentRenderingInstance } from './componentRenderUtils'
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling' import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
import { ComponentPublicInstance } from './componentProxy' import { ComponentPublicInstance } from './componentPublicInstance'
export interface DirectiveBinding<V = any> { export interface DirectiveBinding<V = any> {
instance: ComponentPublicInstance | null instance: ComponentPublicInstance | null

View File

@ -68,11 +68,6 @@ interface Constructor<P = any> {
new (): { $props: P } new (): { $props: P }
} }
// Excludes Component type from returned `defineComponent`
type NotDefinedComponent<T extends Component> = T extends Constructor
? never
: T
// The following is a series of overloads for providing props validation of // The following is a series of overloads for providing props validation of
// manually written render functions. // manually written render functions.
@ -117,9 +112,9 @@ export function h<P, E extends EmitsOptions = {}>(
// catch-all for generic component types // catch-all for generic component types
export function h(type: Component, children?: RawChildren): VNode export function h(type: Component, children?: RawChildren): VNode
// exclude `defineComponent` // exclude `defineComponent` constructors
export function h<Options extends ComponentOptions | FunctionalComponent<{}>>( export function h<T extends ComponentOptions | FunctionalComponent<{}>>(
type: NotDefinedComponent<Options>, type: T,
props?: RawProps | null, props?: RawProps | null,
children?: RawChildren | RawSlots children?: RawChildren | RawSlots
): VNode ): VNode

View File

@ -1,7 +1,7 @@
import { currentRenderingInstance } from '../componentRenderUtils' import { currentRenderingInstance } from '../componentRenderUtils'
import { import {
currentInstance, currentInstance,
Component, ConcreteComponent,
FunctionalComponent, FunctionalComponent,
ComponentOptions ComponentOptions
} from '../component' } from '../component'
@ -16,7 +16,9 @@ const DIRECTIVES = 'directives'
/** /**
* @private * @private
*/ */
export function resolveComponent(name: string): Component | string | undefined { export function resolveComponent(
name: string
): ConcreteComponent | string | undefined {
return resolveAsset(COMPONENTS, name) || name return resolveAsset(COMPONENTS, name) || name
} }
@ -49,7 +51,7 @@ function resolveAsset(
type: typeof COMPONENTS, type: typeof COMPONENTS,
name: string, name: string,
warnMissing?: boolean warnMissing?: boolean
): Component | undefined ): ConcreteComponent | undefined
// overload 2: directives // overload 2: directives
function resolveAsset( function resolveAsset(
type: typeof DIRECTIVES, type: typeof DIRECTIVES,

View File

@ -1,6 +1,6 @@
/* eslint-disable no-restricted-globals */ /* eslint-disable no-restricted-globals */
import { import {
Component, ConcreteComponent,
ComponentInternalInstance, ComponentInternalInstance,
ComponentOptions, ComponentOptions,
InternalRenderFunction InternalRenderFunction
@ -10,7 +10,7 @@ import { extend } from '@vue/shared'
export let isHmrUpdating = false export let isHmrUpdating = false
export const hmrDirtyComponents = new Set<Component>() export const hmrDirtyComponents = new Set<ConcreteComponent>()
export interface HMRRuntime { export interface HMRRuntime {
createRecord: typeof createRecord createRecord: typeof createRecord

View File

@ -159,6 +159,7 @@ export {
} from './vnode' } from './vnode'
export { export {
Component, Component,
ConcreteComponent,
FunctionalComponent, FunctionalComponent,
ComponentInternalInstance, ComponentInternalInstance,
SetupContext, SetupContext,
@ -178,7 +179,7 @@ export {
export { export {
ComponentPublicInstance, ComponentPublicInstance,
ComponentCustomProperties ComponentCustomProperties
} from './componentProxy' } from './componentPublicInstance'
export { export {
Renderer, Renderer,
RendererNode, RendererNode,

View File

@ -66,7 +66,7 @@ import {
import { createHydrationFunctions, RootHydrateFunction } from './hydration' import { createHydrationFunctions, RootHydrateFunction } from './hydration'
import { invokeDirectiveHook } from './directives' import { invokeDirectiveHook } from './directives'
import { startMeasure, endMeasure } from './profiling' import { startMeasure, endMeasure } from './profiling'
import { ComponentPublicInstance } from './componentProxy' import { ComponentPublicInstance } from './componentPublicInstance'
import { devtoolsComponentRemoved, devtoolsComponentUpdated } from './devtools' import { devtoolsComponentRemoved, devtoolsComponentUpdated } from './devtools'
import { initFeatureFlags } from './featureFlags' import { initFeatureFlags } from './featureFlags'

View File

@ -15,8 +15,9 @@ import {
import { import {
ComponentInternalInstance, ComponentInternalInstance,
Data, Data,
Component, ConcreteComponent,
ClassComponent ClassComponent,
Component
} from './component' } from './component'
import { RawSlots } from './componentSlots' import { RawSlots } from './componentSlots'
import { isProxy, Ref, toRaw } from '@vue/reactivity' import { isProxy, Ref, toRaw } from '@vue/reactivity'
@ -244,7 +245,7 @@ export function isSameVNodeType(n1: VNode, n2: VNode): boolean {
if ( if (
__DEV__ && __DEV__ &&
n2.shapeFlag & ShapeFlags.COMPONENT && n2.shapeFlag & ShapeFlags.COMPONENT &&
hmrDirtyComponents.has(n2.type as Component) hmrDirtyComponents.has(n2.type as ConcreteComponent)
) { ) {
// HMR only: if the component has been hot-updated, force a reload. // HMR only: if the component has been hot-updated, force a reload.
return false return false

View File

@ -2,7 +2,7 @@ import { VNode } from './vnode'
import { import {
Data, Data,
ComponentInternalInstance, ComponentInternalInstance,
Component, ConcreteComponent,
formatComponentName formatComponentName
} from './component' } from './component'
import { isString, isFunction } from '@vue/shared' import { isString, isFunction } from '@vue/shared'
@ -10,7 +10,7 @@ import { toRaw, isRef, pauseTracking, resetTracking } from '@vue/reactivity'
import { callWithErrorHandling, ErrorCodes } from './errorHandling' import { callWithErrorHandling, ErrorCodes } from './errorHandling'
type ComponentVNode = VNode & { type ComponentVNode = VNode & {
type: Component type: ConcreteComponent
} }
const stack: VNode[] = [] const stack: VNode[] = []

View File

@ -1,5 +1,6 @@
import { import {
describe, describe,
Component,
defineComponent, defineComponent,
PropType, PropType,
ref, ref,
@ -179,6 +180,8 @@ describe('with object props', () => {
} }
}) })
expectType<Component>(MyComponent)
// Test TSX // Test TSX
expectType<JSX.Element>( expectType<JSX.Element>(
<MyComponent <MyComponent
@ -205,6 +208,17 @@ describe('with object props', () => {
/> />
) )
expectType<Component>(
<MyComponent
b="b"
dd={{ n: 1 }}
ddd={['ddd']}
eee={() => ({ a: 'eee' })}
fff={(a, b) => ({ a: a > +b })}
hhh={false}
/>
)
// @ts-expect-error missing required props // @ts-expect-error missing required props
expectError(<MyComponent />) expectError(<MyComponent />)

View File

@ -148,6 +148,7 @@ describe('h support for generic component type', () => {
function foo(bar: Component) { function foo(bar: Component) {
h(bar) h(bar)
h(bar, 'hello') h(bar, 'hello')
// @ts-expect-error
h(bar, { id: 'ok' }, 'hello') h(bar, { id: 'ok' }, 'hello')
} }
foo({}) foo({})