Skip to content

Commit eb2ae44

Browse files
committed
refactor(types): widen Component type to include consutructor types
returned from `defineComponent` ref: vuejs/router#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.
1 parent 4baf852 commit eb2ae44

21 files changed

+118
-103
lines changed

packages/runtime-core/__tests__/components/Suspense.spec.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ import {
1111
watch,
1212
watchEffect,
1313
onUnmounted,
14-
onErrorCaptured,
15-
Component
14+
onErrorCaptured
1615
} from '@vue/runtime-test'
1716

1817
describe('Suspense', () => {
@@ -31,7 +30,7 @@ describe('Suspense', () => {
3130
setup(props: any, { slots }: any) {
3231
const p = new Promise(resolve => {
3332
setTimeout(() => {
34-
resolve(() => h<Component>(comp, props, slots))
33+
resolve(() => h(comp, props, slots))
3534
}, delay)
3635
})
3736
// in Node 12, due to timer/nextTick mechanism change, we have to wait

packages/runtime-core/src/apiAsyncComponent.ts

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,28 @@
11
import {
2-
PublicAPIComponent,
32
Component,
3+
ConcreteComponent,
44
currentInstance,
55
ComponentInternalInstance,
66
isInSSRComponentSetup
77
} from './component'
88
import { isFunction, isObject } from '@vue/shared'
9-
import { ComponentPublicInstance } from './componentProxy'
9+
import { ComponentPublicInstance } from './componentPublicInstance'
1010
import { createVNode } from './vnode'
1111
import { defineComponent } from './apiDefineComponent'
1212
import { warn } from './warning'
1313
import { ref } from '@vue/reactivity'
1414
import { handleError, ErrorCodes } from './errorHandling'
1515

16-
export type AsyncComponentResolveResult<T = PublicAPIComponent> =
17-
| T
18-
| { default: T } // es modules
16+
export type AsyncComponentResolveResult<T = Component> = T | { default: T } // es modules
1917

2018
export type AsyncComponentLoader<T = any> = () => Promise<
2119
AsyncComponentResolveResult<T>
2220
>
2321

2422
export interface AsyncComponentOptions<T = any> {
2523
loader: AsyncComponentLoader<T>
26-
loadingComponent?: PublicAPIComponent
27-
errorComponent?: PublicAPIComponent
24+
loadingComponent?: Component
25+
errorComponent?: Component
2826
delay?: number
2927
timeout?: number
3028
suspensible?: boolean
@@ -37,7 +35,7 @@ export interface AsyncComponentOptions<T = any> {
3735
}
3836

3937
export function defineAsyncComponent<
40-
T extends PublicAPIComponent = { new (): ComponentPublicInstance }
38+
T extends Component = { new (): ComponentPublicInstance }
4139
>(source: AsyncComponentLoader<T> | AsyncComponentOptions<T>): T {
4240
if (isFunction(source)) {
4341
source = { loader: source }
@@ -53,8 +51,8 @@ export function defineAsyncComponent<
5351
onError: userOnError
5452
} = source
5553

56-
let pendingRequest: Promise<Component> | null = null
57-
let resolvedComp: Component | undefined
54+
let pendingRequest: Promise<ConcreteComponent> | null = null
55+
let resolvedComp: ConcreteComponent | undefined
5856

5957
let retries = 0
6058
const retry = () => {
@@ -63,8 +61,8 @@ export function defineAsyncComponent<
6361
return load()
6462
}
6563

66-
const load = (): Promise<Component> => {
67-
let thisRequest: Promise<Component>
64+
const load = (): Promise<ConcreteComponent> => {
65+
let thisRequest: Promise<ConcreteComponent>
6866
return (
6967
pendingRequest ||
7068
(thisRequest = pendingRequest = loader()
@@ -135,7 +133,9 @@ export function defineAsyncComponent<
135133
onError(err)
136134
return () =>
137135
errorComponent
138-
? createVNode(errorComponent as Component, { error: err })
136+
? createVNode(errorComponent as ConcreteComponent, {
137+
error: err
138+
})
139139
: null
140140
})
141141
}
@@ -175,19 +175,19 @@ export function defineAsyncComponent<
175175
if (loaded.value && resolvedComp) {
176176
return createInnerComp(resolvedComp, instance)
177177
} else if (error.value && errorComponent) {
178-
return createVNode(errorComponent as Component, {
178+
return createVNode(errorComponent as ConcreteComponent, {
179179
error: error.value
180180
})
181181
} else if (loadingComponent && !delayed.value) {
182-
return createVNode(loadingComponent as Component)
182+
return createVNode(loadingComponent as ConcreteComponent)
183183
}
184184
}
185185
}
186186
}) as any
187187
}
188188

189189
function createInnerComp(
190-
comp: Component,
190+
comp: ConcreteComponent,
191191
{ vnode: { props, children } }: ComponentInternalInstance
192192
) {
193193
return createVNode(comp, props, children)

packages/runtime-core/src/apiCreateApp.ts

+14-11
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import {
2-
Component,
2+
ConcreteComponent,
33
Data,
44
validateComponentName,
5-
PublicAPIComponent
5+
Component
66
} from './component'
77
import { ComponentOptions } from './componentOptions'
8-
import { ComponentPublicInstance } from './componentProxy'
8+
import { ComponentPublicInstance } from './componentPublicInstance'
99
import { Directive, validateDirectiveName } from './directives'
1010
import { RootRenderFunction } from './renderer'
1111
import { InjectionKey } from './apiInject'
@@ -21,8 +21,8 @@ export interface App<HostElement = any> {
2121
config: AppConfig
2222
use(plugin: Plugin, ...options: any[]): this
2323
mixin(mixin: ComponentOptions): this
24-
component(name: string): PublicAPIComponent | undefined
25-
component(name: string, component: PublicAPIComponent): this
24+
component(name: string): Component | undefined
25+
component(name: string, component: Component): this
2626
directive(name: string): Directive | undefined
2727
directive(name: string, directive: Directive): this
2828
mount(
@@ -33,7 +33,7 @@ export interface App<HostElement = any> {
3333
provide<T>(key: InjectionKey<T> | string, value: T): this
3434

3535
// internal, but we need to expose these for the server-renderer and devtools
36-
_component: Component
36+
_component: ConcreteComponent
3737
_props: Data | null
3838
_container: HostElement | null
3939
_context: AppContext
@@ -70,7 +70,7 @@ export interface AppContext {
7070
app: App // for devtools
7171
config: AppConfig
7272
mixins: ComponentOptions[]
73-
components: Record<string, PublicAPIComponent>
73+
components: Record<string, Component>
7474
directives: Record<string, Directive>
7575
provides: Record<string | symbol, any>
7676
reload?: () => void // HMR only
@@ -104,7 +104,7 @@ export function createAppContext(): AppContext {
104104
}
105105

106106
export type CreateAppFunction<HostElement> = (
107-
rootComponent: PublicAPIComponent,
107+
rootComponent: Component,
108108
rootProps?: Data | null
109109
) => App<HostElement>
110110

@@ -124,7 +124,7 @@ export function createAppAPI<HostElement>(
124124
let isMounted = false
125125

126126
const app: App = (context.app = {
127-
_component: rootComponent as Component,
127+
_component: rootComponent as ConcreteComponent,
128128
_props: rootProps,
129129
_container: null,
130130
_context: context,
@@ -177,7 +177,7 @@ export function createAppAPI<HostElement>(
177177
return app
178178
},
179179

180-
component(name: string, component?: PublicAPIComponent): any {
180+
component(name: string, component?: Component): any {
181181
if (__DEV__) {
182182
validateComponentName(name, context.config)
183183
}
@@ -208,7 +208,10 @@ export function createAppAPI<HostElement>(
208208

209209
mount(rootContainer: HostElement, isHydrate?: boolean): any {
210210
if (!isMounted) {
211-
const vnode = createVNode(rootComponent as Component, rootProps)
211+
const vnode = createVNode(
212+
rootComponent as ConcreteComponent,
213+
rootProps
214+
)
212215
// store app context on the root VNode.
213216
// this will be set on the root instance on initial mount.
214217
vnode.appContext = context

packages/runtime-core/src/apiDefineComponent.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
import {
1717
CreateComponentPublicInstance,
1818
ComponentPublicInstanceConstructor
19-
} from './componentProxy'
19+
} from './componentPublicInstance'
2020
import { ExtractPropTypes, ComponentPropsOptions } from './componentProps'
2121
import { EmitsOptions } from './componentEmits'
2222
import { isFunction } from '@vue/shared'
@@ -205,7 +205,5 @@ export function defineComponent<
205205

206206
// implementation, close to no-op
207207
export function defineComponent(options: unknown) {
208-
return isFunction(options)
209-
? { setup: options, name: options.name }
210-
: options
208+
return isFunction(options) ? { setup: options, name: options.name } : options
211209
}

packages/runtime-core/src/apiLifecycle.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
setCurrentInstance,
66
isInSSRComponentSetup
77
} from './component'
8-
import { ComponentPublicInstance } from './componentProxy'
8+
import { ComponentPublicInstance } from './componentPublicInstance'
99
import { callWithAsyncErrorHandling, ErrorTypeStrings } from './errorHandling'
1010
import { warn } from './warning'
1111
import { capitalize } from '@vue/shared'

packages/runtime-core/src/component.ts

+19-21
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import {
77
proxyRefs
88
} from '@vue/reactivity'
99
import {
10-
CreateComponentPublicInstance,
1110
ComponentPublicInstance,
1211
PublicInstanceProxyHandlers,
1312
RuntimeCompiledPublicInstanceProxyHandlers,
1413
createRenderContext,
1514
exposePropsOnRenderContext,
16-
exposeSetupStateOnRenderContext
17-
} from './componentProxy'
15+
exposeSetupStateOnRenderContext,
16+
ComponentPublicInstanceConstructor
17+
} from './componentPublicInstance'
1818
import {
1919
ComponentPropsOptions,
2020
NormalizedPropsOptions,
@@ -110,21 +110,19 @@ export interface ClassComponent {
110110
__vccOpts: ComponentOptions
111111
}
112112

113-
export type Component = ComponentOptions | FunctionalComponent<any, any>
114-
115-
// A type used in public APIs where a component type is expected.
116-
// The constructor type is an artificial type returned by defineComponent().
117-
export type PublicAPIComponent =
118-
| Component
119-
| {
120-
new (...args: any[]): CreateComponentPublicInstance<
121-
any,
122-
any,
123-
any,
124-
any,
125-
any
126-
>
127-
}
113+
/**
114+
* Concrete component type matches its actual value: it's either an options
115+
* object, or a function. Use this where the code expects to work with actual
116+
* values, e.g. checking if its a function or not. This is mostly for internal
117+
* implementation code.
118+
*/
119+
export type ConcreteComponent = ComponentOptions | FunctionalComponent<any, any>
120+
121+
/**
122+
* A type used in public APIs where a component type is expected.
123+
* The constructor type is an artificial type returned by defineComponent().
124+
*/
125+
export type Component = ConcreteComponent | ComponentPublicInstanceConstructor
128126

129127
export { ComponentOptions }
130128

@@ -174,7 +172,7 @@ export type InternalRenderFunction = {
174172
*/
175173
export interface ComponentInternalInstance {
176174
uid: number
177-
type: Component
175+
type: ConcreteComponent
178176
parent: ComponentInternalInstance | null
179177
root: ComponentInternalInstance
180178
appContext: AppContext
@@ -346,7 +344,7 @@ export function createComponentInstance(
346344
parent: ComponentInternalInstance | null,
347345
suspense: SuspenseBoundary | null
348346
) {
349-
const type = vnode.type as Component
347+
const type = vnode.type as ConcreteComponent
350348
// inherit parent app context - or - if root, adopt from root vnode
351349
const appContext =
352350
(parent ? parent.appContext : vnode.appContext) || emptyAppContext
@@ -703,7 +701,7 @@ const classify = (str: string): string =>
703701
/* istanbul ignore next */
704702
export function formatComponentName(
705703
instance: ComponentInternalInstance | null,
706-
Component: Component,
704+
Component: ConcreteComponent,
707705
isRoot = false
708706
): string {
709707
let name = isFunction(Component)

packages/runtime-core/src/componentEmits.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
isFunction,
99
extend
1010
} from '@vue/shared'
11-
import { ComponentInternalInstance, Component } from './component'
11+
import { ComponentInternalInstance, ConcreteComponent } from './component'
1212
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
1313
import { warn } from './warning'
1414
import { normalizePropsOptions } from './componentProps'
@@ -94,7 +94,7 @@ export function emit(
9494
}
9595

9696
function normalizeEmitsOptions(
97-
comp: Component
97+
comp: ConcreteComponent
9898
): ObjectEmitsOptions | undefined {
9999
if (hasOwn(comp, '__emits')) {
100100
return comp.__emits
@@ -131,7 +131,7 @@ function normalizeEmitsOptions(
131131
// Check if an incoming prop key is a declared emit event listener.
132132
// e.g. With `emits: { click: null }`, props named `onClick` and `onclick` are
133133
// both considered matched listeners.
134-
export function isEmitListener(comp: Component, key: string): boolean {
134+
export function isEmitListener(comp: ConcreteComponent, key: string): boolean {
135135
let emits: ObjectEmitsOptions | undefined
136136
if (!isOn(key) || !(emits = normalizeEmitsOptions(comp))) {
137137
return false

packages/runtime-core/src/componentOptions.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import {
33
Data,
44
SetupContext,
55
ComponentInternalOptions,
6-
PublicAPIComponent,
76
Component,
7+
ConcreteComponent,
88
InternalRenderFunction
99
} from './component'
1010
import {
@@ -52,7 +52,7 @@ import { Directive } from './directives'
5252
import {
5353
CreateComponentPublicInstance,
5454
ComponentPublicInstance
55-
} from './componentProxy'
55+
} from './componentPublicInstance'
5656
import { warn } from './warning'
5757
import { VNodeChild } from './vnode'
5858

@@ -103,7 +103,7 @@ export interface ComponentOptionsBase<
103103
// Luckily `render()` doesn't need any arguments nor does it care about return
104104
// type.
105105
render?: Function
106-
components?: Record<string, PublicAPIComponent>
106+
components?: Record<string, Component>
107107
directives?: Record<string, Directive>
108108
inheritAttrs?: boolean
109109
emits?: (E | EE[]) & ThisType<void>
@@ -132,7 +132,7 @@ export interface ComponentOptionsBase<
132132
* marker for AsyncComponentWrapper
133133
* @internal
134134
*/
135-
__asyncLoader?: () => Promise<Component>
135+
__asyncLoader?: () => Promise<ConcreteComponent>
136136
/**
137137
* cache for merged $options
138138
* @internal

0 commit comments

Comments
 (0)