types(runtime-core): refactor defineComponent (#1883)
This commit is contained in:
parent
848ccf56fb
commit
4fd468aced
@ -32,17 +32,17 @@ const readonlyGet = /*#__PURE__*/ createGetter(true)
|
|||||||
const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
|
const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
|
||||||
|
|
||||||
const arrayInstrumentations: Record<string, Function> = {}
|
const arrayInstrumentations: Record<string, Function> = {}
|
||||||
;['includes', 'indexOf', 'lastIndexOf'].forEach(key => {
|
;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => {
|
||||||
arrayInstrumentations[key] = function(...args: any[]): any {
|
arrayInstrumentations[key] = function(this: unknown[], ...args: unknown[]) {
|
||||||
const arr = toRaw(this) as any
|
const arr = toRaw(this)
|
||||||
for (let i = 0, l = (this as any).length; i < l; i++) {
|
for (let i = 0, l = this.length; i < l; i++) {
|
||||||
track(arr, TrackOpTypes.GET, i + '')
|
track(arr, TrackOpTypes.GET, i + '')
|
||||||
}
|
}
|
||||||
// we run the method using the original args first (which may be reactive)
|
// we run the method using the original args first (which may be reactive)
|
||||||
const res = arr[key](...args)
|
const res = (arr[key] as any)(...args)
|
||||||
if (res === -1 || res === false) {
|
if (res === -1 || res === false) {
|
||||||
// if that didn't work, run it again using raw values.
|
// if that didn't work, run it again using raw values.
|
||||||
return arr[key](...args.map(toRaw))
|
return (arr[key] as any)(...args.map(toRaw))
|
||||||
} else {
|
} else {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@ -6,22 +6,65 @@ import {
|
|||||||
ComponentOptionsWithObjectProps,
|
ComponentOptionsWithObjectProps,
|
||||||
ComponentOptionsMixin,
|
ComponentOptionsMixin,
|
||||||
RenderFunction,
|
RenderFunction,
|
||||||
UnwrapAsyncBindings
|
ComponentOptionsBase
|
||||||
} from './componentOptions'
|
} from './componentOptions'
|
||||||
import {
|
import {
|
||||||
SetupContext,
|
SetupContext,
|
||||||
FunctionalComponent,
|
|
||||||
AllowedComponentProps,
|
AllowedComponentProps,
|
||||||
ComponentCustomProps
|
ComponentCustomProps
|
||||||
} from './component'
|
} from './component'
|
||||||
import {
|
|
||||||
CreateComponentPublicInstance,
|
|
||||||
ComponentPublicInstanceConstructor
|
|
||||||
} 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'
|
||||||
import { VNodeProps } from './vnode'
|
import { VNodeProps } from './vnode'
|
||||||
|
import {
|
||||||
|
CreateComponentPublicInstance,
|
||||||
|
ComponentPublicInstanceConstructor
|
||||||
|
} from './componentPublicInstance'
|
||||||
|
|
||||||
|
export type PublicProps = VNodeProps &
|
||||||
|
AllowedComponentProps &
|
||||||
|
ComponentCustomProps
|
||||||
|
|
||||||
|
export type DefineComponent<
|
||||||
|
PropsOrPropOptions = any,
|
||||||
|
RawBindings = any,
|
||||||
|
D = any,
|
||||||
|
C extends ComputedOptions = ComputedOptions,
|
||||||
|
M extends MethodOptions = MethodOptions,
|
||||||
|
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
|
E extends EmitsOptions = Record<string, any>,
|
||||||
|
EE extends string = string,
|
||||||
|
PP = PublicProps,
|
||||||
|
RequiredProps = Readonly<ExtractPropTypes<PropsOrPropOptions>>,
|
||||||
|
OptionalProps = Readonly<ExtractPropTypes<PropsOrPropOptions, false>>
|
||||||
|
> = ComponentPublicInstanceConstructor<
|
||||||
|
CreateComponentPublicInstance<
|
||||||
|
OptionalProps,
|
||||||
|
RawBindings,
|
||||||
|
D,
|
||||||
|
C,
|
||||||
|
M,
|
||||||
|
Mixin,
|
||||||
|
Extends,
|
||||||
|
E,
|
||||||
|
PP & OptionalProps
|
||||||
|
> &
|
||||||
|
RequiredProps
|
||||||
|
> &
|
||||||
|
ComponentOptionsBase<
|
||||||
|
RequiredProps,
|
||||||
|
RawBindings,
|
||||||
|
D,
|
||||||
|
C,
|
||||||
|
M,
|
||||||
|
Mixin,
|
||||||
|
Extends,
|
||||||
|
E,
|
||||||
|
EE
|
||||||
|
> &
|
||||||
|
PP
|
||||||
|
|
||||||
// defineComponent is a utility that is primarily used for type inference
|
// defineComponent is a utility that is primarily used for type inference
|
||||||
// when declaring components. Type inference is provided in the component
|
// when declaring components. Type inference is provided in the component
|
||||||
@ -35,21 +78,7 @@ export function defineComponent<Props, RawBindings = object>(
|
|||||||
props: Readonly<Props>,
|
props: Readonly<Props>,
|
||||||
ctx: SetupContext
|
ctx: SetupContext
|
||||||
) => RawBindings | RenderFunction
|
) => RawBindings | RenderFunction
|
||||||
): ComponentPublicInstanceConstructor<
|
): DefineComponent<Props, RawBindings>
|
||||||
CreateComponentPublicInstance<
|
|
||||||
Props,
|
|
||||||
UnwrapAsyncBindings<RawBindings>,
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
// public props
|
|
||||||
VNodeProps & Props & AllowedComponentProps & ComponentCustomProps
|
|
||||||
>
|
|
||||||
> &
|
|
||||||
FunctionalComponent<Props>
|
|
||||||
|
|
||||||
// overload 2: object format with no props
|
// overload 2: object format with no props
|
||||||
// (uses user defined props interface)
|
// (uses user defined props interface)
|
||||||
@ -58,11 +87,11 @@ export function defineComponent<
|
|||||||
Props = {},
|
Props = {},
|
||||||
RawBindings = {},
|
RawBindings = {},
|
||||||
D = {},
|
D = {},
|
||||||
C extends ComputedOptions = {},
|
C extends ComputedOptions = ComputedOptions,
|
||||||
M extends MethodOptions = {},
|
M extends MethodOptions = MethodOptions,
|
||||||
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
E extends EmitsOptions = Record<string, any>,
|
E extends EmitsOptions = EmitsOptions,
|
||||||
EE extends string = string
|
EE extends string = string
|
||||||
>(
|
>(
|
||||||
options: ComponentOptionsWithoutProps<
|
options: ComponentOptionsWithoutProps<
|
||||||
@ -76,30 +105,7 @@ export function defineComponent<
|
|||||||
E,
|
E,
|
||||||
EE
|
EE
|
||||||
>
|
>
|
||||||
): ComponentPublicInstanceConstructor<
|
): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE>
|
||||||
CreateComponentPublicInstance<
|
|
||||||
Props,
|
|
||||||
UnwrapAsyncBindings<RawBindings>,
|
|
||||||
D,
|
|
||||||
C,
|
|
||||||
M,
|
|
||||||
Mixin,
|
|
||||||
Extends,
|
|
||||||
E,
|
|
||||||
VNodeProps & Props & AllowedComponentProps & ComponentCustomProps
|
|
||||||
>
|
|
||||||
> &
|
|
||||||
ComponentOptionsWithoutProps<
|
|
||||||
Props,
|
|
||||||
RawBindings,
|
|
||||||
D,
|
|
||||||
C,
|
|
||||||
M,
|
|
||||||
Mixin,
|
|
||||||
Extends,
|
|
||||||
E,
|
|
||||||
EE
|
|
||||||
>
|
|
||||||
|
|
||||||
// overload 3: object format with array props declaration
|
// overload 3: object format with array props declaration
|
||||||
// props inferred as { [key in PropNames]?: any }
|
// props inferred as { [key in PropNames]?: any }
|
||||||
@ -126,32 +132,17 @@ export function defineComponent<
|
|||||||
E,
|
E,
|
||||||
EE
|
EE
|
||||||
>
|
>
|
||||||
): ComponentPublicInstanceConstructor<
|
): DefineComponent<
|
||||||
// array props technically doesn't place any constraints on props in TSX before,
|
Readonly<{ [key in PropNames]?: any }>,
|
||||||
// but now we can export array props in TSX
|
RawBindings,
|
||||||
CreateComponentPublicInstance<
|
D,
|
||||||
Readonly<{ [key in PropNames]?: any }>,
|
C,
|
||||||
UnwrapAsyncBindings<RawBindings>,
|
M,
|
||||||
D,
|
Mixin,
|
||||||
C,
|
Extends,
|
||||||
M,
|
E,
|
||||||
Mixin,
|
EE
|
||||||
Extends,
|
>
|
||||||
E,
|
|
||||||
AllowedComponentProps & ComponentCustomProps
|
|
||||||
>
|
|
||||||
> &
|
|
||||||
ComponentOptionsWithArrayProps<
|
|
||||||
PropNames,
|
|
||||||
RawBindings,
|
|
||||||
D,
|
|
||||||
C,
|
|
||||||
M,
|
|
||||||
Mixin,
|
|
||||||
Extends,
|
|
||||||
E,
|
|
||||||
EE
|
|
||||||
>
|
|
||||||
|
|
||||||
// overload 4: object format with object props declaration
|
// overload 4: object format with object props declaration
|
||||||
// see `ExtractPropTypes` in ./componentProps.ts
|
// see `ExtractPropTypes` in ./componentProps.ts
|
||||||
@ -179,33 +170,20 @@ export function defineComponent<
|
|||||||
E,
|
E,
|
||||||
EE
|
EE
|
||||||
>
|
>
|
||||||
): ComponentPublicInstanceConstructor<
|
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
|
||||||
CreateComponentPublicInstance<
|
|
||||||
ExtractPropTypes<PropsOptions, false>,
|
|
||||||
UnwrapAsyncBindings<RawBindings>,
|
|
||||||
D,
|
|
||||||
C,
|
|
||||||
M,
|
|
||||||
Mixin,
|
|
||||||
Extends,
|
|
||||||
E,
|
|
||||||
VNodeProps & AllowedComponentProps & ComponentCustomProps
|
|
||||||
> &
|
|
||||||
Readonly<ExtractPropTypes<PropsOptions>>
|
|
||||||
> &
|
|
||||||
ComponentOptionsWithObjectProps<
|
|
||||||
PropsOptions,
|
|
||||||
RawBindings,
|
|
||||||
D,
|
|
||||||
C,
|
|
||||||
M,
|
|
||||||
Mixin,
|
|
||||||
Extends,
|
|
||||||
E,
|
|
||||||
EE
|
|
||||||
>
|
|
||||||
|
|
||||||
// implementation, close to no-op
|
// implementation, close to no-op
|
||||||
export function defineComponent(options: unknown) {
|
export function defineComponent(options: unknown) {
|
||||||
return isFunction(options) ? { setup: options, name: options.name } : options
|
return isFunction(options) ? { setup: options, name: options.name } : options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defineComponent({
|
||||||
|
async setup() {
|
||||||
|
return {
|
||||||
|
a: 123
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
this.a
|
||||||
|
}
|
||||||
|
})
|
||||||
|
@ -26,7 +26,12 @@ import { warn } from './warning'
|
|||||||
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
|
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
|
||||||
import { AppContext, createAppContext, AppConfig } from './apiCreateApp'
|
import { AppContext, createAppContext, AppConfig } from './apiCreateApp'
|
||||||
import { Directive, validateDirectiveName } from './directives'
|
import { Directive, validateDirectiveName } from './directives'
|
||||||
import { applyOptions, ComponentOptions } from './componentOptions'
|
import {
|
||||||
|
applyOptions,
|
||||||
|
ComponentOptions,
|
||||||
|
ComputedOptions,
|
||||||
|
MethodOptions
|
||||||
|
} from './componentOptions'
|
||||||
import {
|
import {
|
||||||
EmitsOptions,
|
EmitsOptions,
|
||||||
ObjectEmitsOptions,
|
ObjectEmitsOptions,
|
||||||
@ -118,13 +123,29 @@ export interface ClassComponent {
|
|||||||
* values, e.g. checking if its a function or not. This is mostly for internal
|
* values, e.g. checking if its a function or not. This is mostly for internal
|
||||||
* implementation code.
|
* implementation code.
|
||||||
*/
|
*/
|
||||||
export type ConcreteComponent = ComponentOptions | FunctionalComponent<any, any>
|
export type ConcreteComponent<
|
||||||
|
Props = {},
|
||||||
|
RawBindings = any,
|
||||||
|
D = any,
|
||||||
|
C extends ComputedOptions = ComputedOptions,
|
||||||
|
M extends MethodOptions = MethodOptions
|
||||||
|
> =
|
||||||
|
| ComponentOptions<Props, RawBindings, D, C, M>
|
||||||
|
| FunctionalComponent<Props, any>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A type used in public APIs where a component type is expected.
|
* A type used in public APIs where a component type is expected.
|
||||||
* The constructor type is an artificial type returned by defineComponent().
|
* The constructor type is an artificial type returned by defineComponent().
|
||||||
*/
|
*/
|
||||||
export type Component = ConcreteComponent | ComponentPublicInstanceConstructor
|
export type Component<
|
||||||
|
Props = any,
|
||||||
|
RawBindings = any,
|
||||||
|
D = any,
|
||||||
|
C extends ComputedOptions = ComputedOptions,
|
||||||
|
M extends MethodOptions = MethodOptions
|
||||||
|
> =
|
||||||
|
| ConcreteComponent<Props, RawBindings, D, C, M>
|
||||||
|
| ComponentPublicInstanceConstructor<Props>
|
||||||
|
|
||||||
export { ComponentOptions }
|
export { ComponentOptions }
|
||||||
|
|
||||||
|
@ -72,8 +72,6 @@ export interface ComponentCustomOptions {}
|
|||||||
|
|
||||||
export type RenderFunction = () => VNodeChild
|
export type RenderFunction = () => VNodeChild
|
||||||
|
|
||||||
export type UnwrapAsyncBindings<T> = T extends Promise<infer S> ? S : T
|
|
||||||
|
|
||||||
export interface ComponentOptionsBase<
|
export interface ComponentOptionsBase<
|
||||||
Props,
|
Props,
|
||||||
RawBindings,
|
RawBindings,
|
||||||
@ -92,7 +90,7 @@ export interface ComponentOptionsBase<
|
|||||||
this: void,
|
this: void,
|
||||||
props: Props,
|
props: Props,
|
||||||
ctx: SetupContext<E>
|
ctx: SetupContext<E>
|
||||||
) => RawBindings | RenderFunction | void
|
) => Promise<RawBindings> | RawBindings | RenderFunction | void
|
||||||
name?: string
|
name?: string
|
||||||
template?: string | object // can be a direct DOM node
|
template?: string | object // can be a direct DOM node
|
||||||
// Note: we are intentionally using the signature-less `Function` type here
|
// Note: we are intentionally using the signature-less `Function` type here
|
||||||
@ -230,10 +228,29 @@ export type ComponentOptionsWithObjectProps<
|
|||||||
>
|
>
|
||||||
>
|
>
|
||||||
|
|
||||||
export type ComponentOptions =
|
export type ComponentOptions<
|
||||||
| ComponentOptionsWithoutProps<any, any, any, any, any>
|
Props = {},
|
||||||
| ComponentOptionsWithObjectProps<any, any, any, any, any>
|
RawBindings = any,
|
||||||
| ComponentOptionsWithArrayProps<any, any, any, any, any>
|
D = any,
|
||||||
|
C extends ComputedOptions = any,
|
||||||
|
M extends MethodOptions = any,
|
||||||
|
Mixin extends ComponentOptionsMixin = any,
|
||||||
|
Extends extends ComponentOptionsMixin = any,
|
||||||
|
E extends EmitsOptions = any
|
||||||
|
> = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E> &
|
||||||
|
ThisType<
|
||||||
|
CreateComponentPublicInstance<
|
||||||
|
{},
|
||||||
|
RawBindings,
|
||||||
|
D,
|
||||||
|
C,
|
||||||
|
M,
|
||||||
|
Mixin,
|
||||||
|
Extends,
|
||||||
|
E,
|
||||||
|
Readonly<Props>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
|
||||||
export type ComponentOptionsMixin = ComponentOptionsBase<
|
export type ComponentOptionsMixin = ComponentOptionsBase<
|
||||||
any,
|
any,
|
||||||
@ -638,17 +655,13 @@ export function applyOptions(
|
|||||||
onRenderTriggered(renderTriggered.bind(publicThis))
|
onRenderTriggered(renderTriggered.bind(publicThis))
|
||||||
}
|
}
|
||||||
if (__DEV__ && beforeDestroy) {
|
if (__DEV__ && beforeDestroy) {
|
||||||
warn(
|
warn(`\`beforeDestroy\` has been renamed to \`beforeUnmount\`.`)
|
||||||
`\`beforeDestroy\` has been renamed to \`beforeUnmount\`.`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if (beforeUnmount) {
|
if (beforeUnmount) {
|
||||||
onBeforeUnmount(beforeUnmount.bind(publicThis))
|
onBeforeUnmount(beforeUnmount.bind(publicThis))
|
||||||
}
|
}
|
||||||
if (__DEV__ && destroyed) {
|
if (__DEV__ && destroyed) {
|
||||||
warn(
|
warn(`\`destroyed\` has been renamed to \`unmounted\`.`)
|
||||||
`\`destroyed\` has been renamed to \`unmounted\`.`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if (unmounted) {
|
if (unmounted) {
|
||||||
onUnmounted(unmounted.bind(publicThis))
|
onUnmounted(unmounted.bind(publicThis))
|
||||||
|
@ -27,8 +27,7 @@ import {
|
|||||||
OptionTypesType,
|
OptionTypesType,
|
||||||
OptionTypesKeys,
|
OptionTypesKeys,
|
||||||
resolveMergedOptions,
|
resolveMergedOptions,
|
||||||
isInBeforeCreate,
|
isInBeforeCreate
|
||||||
UnwrapAsyncBindings
|
|
||||||
} from './componentOptions'
|
} from './componentOptions'
|
||||||
import { EmitsOptions, EmitFn } from './componentEmits'
|
import { EmitsOptions, EmitFn } from './componentEmits'
|
||||||
import { Slots } from './componentSlots'
|
import { Slots } from './componentSlots'
|
||||||
@ -102,7 +101,18 @@ type UnwrapMixinsType<
|
|||||||
type EnsureNonVoid<T> = T extends void ? {} : T
|
type EnsureNonVoid<T> = T extends void ? {} : T
|
||||||
|
|
||||||
export type ComponentPublicInstanceConstructor<
|
export type ComponentPublicInstanceConstructor<
|
||||||
T extends ComponentPublicInstance = ComponentPublicInstance<any>
|
T extends ComponentPublicInstance<
|
||||||
|
Props,
|
||||||
|
RawBindings,
|
||||||
|
D,
|
||||||
|
C,
|
||||||
|
M
|
||||||
|
> = ComponentPublicInstance<any>,
|
||||||
|
Props = any,
|
||||||
|
RawBindings = any,
|
||||||
|
D = any,
|
||||||
|
C extends ComputedOptions = ComputedOptions,
|
||||||
|
M extends MethodOptions = MethodOptions
|
||||||
> = {
|
> = {
|
||||||
__isFragment?: never
|
__isFragment?: never
|
||||||
__isTeleport?: never
|
__isTeleport?: never
|
||||||
@ -138,6 +148,7 @@ export type CreateComponentPublicInstance<
|
|||||||
PublicProps,
|
PublicProps,
|
||||||
ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E>
|
ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E>
|
||||||
>
|
>
|
||||||
|
|
||||||
// public properties exposed on the proxy, which is used as the render context
|
// public properties exposed on the proxy, which is used as the render context
|
||||||
// in templates (as `this` in the render option)
|
// in templates (as `this` in the render option)
|
||||||
export type ComponentPublicInstance<
|
export type ComponentPublicInstance<
|
||||||
@ -169,7 +180,7 @@ export type ComponentPublicInstance<
|
|||||||
options?: WatchOptions
|
options?: WatchOptions
|
||||||
): WatchStopHandle
|
): WatchStopHandle
|
||||||
} & P &
|
} & P &
|
||||||
ShallowUnwrapRef<UnwrapAsyncBindings<B>> &
|
ShallowUnwrapRef<B> &
|
||||||
D &
|
D &
|
||||||
ExtractComputedReturns<C> &
|
ExtractComputedReturns<C> &
|
||||||
M &
|
M &
|
||||||
|
@ -10,9 +10,9 @@ import { Teleport, TeleportProps } from './components/Teleport'
|
|||||||
import { Suspense, SuspenseProps } from './components/Suspense'
|
import { Suspense, SuspenseProps } from './components/Suspense'
|
||||||
import { isObject, isArray } from '@vue/shared'
|
import { isObject, isArray } from '@vue/shared'
|
||||||
import { RawSlots } from './componentSlots'
|
import { RawSlots } from './componentSlots'
|
||||||
import { FunctionalComponent, Component } from './component'
|
import { FunctionalComponent, Component, ComponentOptions } from './component'
|
||||||
import { ComponentOptions } from './componentOptions'
|
|
||||||
import { EmitsOptions } from './componentEmits'
|
import { EmitsOptions } from './componentEmits'
|
||||||
|
import { DefineComponent } from './apiDefineComponent'
|
||||||
|
|
||||||
// `h` is a more user-friendly version of `createVNode` that allows omitting the
|
// `h` is a more user-friendly version of `createVNode` that allows omitting the
|
||||||
// props when possible. It is intended for manually written render functions.
|
// props when possible. It is intended for manually written render functions.
|
||||||
@ -50,7 +50,7 @@ type RawProps = VNodeProps & {
|
|||||||
__v_isVNode?: never
|
__v_isVNode?: never
|
||||||
// used to differ from Array children
|
// used to differ from Array children
|
||||||
[Symbol.iterator]?: never
|
[Symbol.iterator]?: never
|
||||||
} & { [key: string]: any }
|
} & Record<string, any>
|
||||||
|
|
||||||
type RawChildren =
|
type RawChildren =
|
||||||
| string
|
| string
|
||||||
@ -112,10 +112,17 @@ 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
|
||||||
|
|
||||||
|
// component without props
|
||||||
|
export function h(
|
||||||
|
type: Component,
|
||||||
|
props: null,
|
||||||
|
children?: RawChildren | RawSlots
|
||||||
|
): VNode
|
||||||
|
|
||||||
// exclude `defineComponent` constructors
|
// exclude `defineComponent` constructors
|
||||||
export function h<T extends ComponentOptions | FunctionalComponent<{}>>(
|
export function h<P>(
|
||||||
type: T,
|
type: ComponentOptions<P>,
|
||||||
props?: RawProps | null,
|
props?: (RawProps & P) | ({} extends P ? null : never),
|
||||||
children?: RawChildren | RawSlots
|
children?: RawChildren | RawSlots
|
||||||
): VNode
|
): VNode
|
||||||
|
|
||||||
@ -127,6 +134,14 @@ export function h<P>(
|
|||||||
children?: RawChildren | RawSlots
|
children?: RawChildren | RawSlots
|
||||||
): VNode
|
): VNode
|
||||||
|
|
||||||
|
// fake constructor type returned by `defineComponent`
|
||||||
|
export function h(type: DefineComponent, children?: RawChildren): VNode
|
||||||
|
export function h<P>(
|
||||||
|
type: DefineComponent<P>,
|
||||||
|
props?: (RawProps & P) | ({} extends P ? null : never),
|
||||||
|
children?: RawChildren | RawSlots
|
||||||
|
): VNode
|
||||||
|
|
||||||
// Actual implementation
|
// Actual implementation
|
||||||
export function h(type: any, propsOrChildren?: any, children?: any): VNode {
|
export function h(type: any, propsOrChildren?: any, children?: any): VNode {
|
||||||
const l = arguments.length
|
const l = arguments.length
|
||||||
|
@ -41,7 +41,7 @@ export {
|
|||||||
} from './apiLifecycle'
|
} from './apiLifecycle'
|
||||||
export { provide, inject } from './apiInject'
|
export { provide, inject } from './apiInject'
|
||||||
export { nextTick } from './scheduler'
|
export { nextTick } from './scheduler'
|
||||||
export { defineComponent } from './apiDefineComponent'
|
export { defineComponent, DefineComponent } from './apiDefineComponent'
|
||||||
export { defineAsyncComponent } from './apiAsyncComponent'
|
export { defineAsyncComponent } from './apiAsyncComponent'
|
||||||
|
|
||||||
// Advanced API ----------------------------------------------------------------
|
// Advanced API ----------------------------------------------------------------
|
||||||
|
413
test-dts/component.test-d.ts
Normal file
413
test-dts/component.test-d.ts
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
import {
|
||||||
|
describe,
|
||||||
|
Component,
|
||||||
|
defineComponent,
|
||||||
|
PropType,
|
||||||
|
ref,
|
||||||
|
Ref,
|
||||||
|
expectError,
|
||||||
|
expectType,
|
||||||
|
ShallowUnwrapRef,
|
||||||
|
FunctionalComponent,
|
||||||
|
ComponentPublicInstance
|
||||||
|
} from './index'
|
||||||
|
|
||||||
|
declare function extractComponentOptions<Props, RawBindings>(
|
||||||
|
obj: Component<Props, RawBindings>
|
||||||
|
): {
|
||||||
|
props: Props
|
||||||
|
rawBindings: RawBindings
|
||||||
|
setup: ShallowUnwrapRef<RawBindings>
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('object props', () => {
|
||||||
|
interface ExpectedProps {
|
||||||
|
a?: number | undefined
|
||||||
|
b: string
|
||||||
|
e?: Function
|
||||||
|
bb: string
|
||||||
|
bbb: string
|
||||||
|
cc?: string[] | undefined
|
||||||
|
dd: { n: 1 }
|
||||||
|
ee?: () => string
|
||||||
|
ff?: (a: number, b: string) => { a: boolean }
|
||||||
|
ccc?: string[] | undefined
|
||||||
|
ddd: string[]
|
||||||
|
eee: () => { a: string }
|
||||||
|
fff: (a: number, b: string) => { a: boolean }
|
||||||
|
hhh: boolean
|
||||||
|
ggg: 'foo' | 'bar'
|
||||||
|
ffff: (a: number, b: string) => { a: boolean }
|
||||||
|
validated?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('defineComponent', () => {
|
||||||
|
const MyComponent = defineComponent({
|
||||||
|
props: {
|
||||||
|
a: Number,
|
||||||
|
// required should make property non-void
|
||||||
|
b: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
e: Function,
|
||||||
|
// default value should infer type and make it non-void
|
||||||
|
bb: {
|
||||||
|
default: 'hello'
|
||||||
|
},
|
||||||
|
bbb: {
|
||||||
|
// Note: default function value requires arrow syntax + explicit
|
||||||
|
// annotation
|
||||||
|
default: (props: any) => (props.bb as string) || 'foo'
|
||||||
|
},
|
||||||
|
// explicit type casting
|
||||||
|
cc: Array as PropType<string[]>,
|
||||||
|
// required + type casting
|
||||||
|
dd: {
|
||||||
|
type: Object as PropType<{ n: 1 }>,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
// return type
|
||||||
|
ee: Function as PropType<() => string>,
|
||||||
|
// arguments + object return
|
||||||
|
ff: Function as PropType<(a: number, b: string) => { a: boolean }>,
|
||||||
|
// explicit type casting with constructor
|
||||||
|
ccc: Array as () => string[],
|
||||||
|
// required + contructor type casting
|
||||||
|
ddd: {
|
||||||
|
type: Array as () => string[],
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
// required + object return
|
||||||
|
eee: {
|
||||||
|
type: Function as PropType<() => { a: string }>,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
// required + arguments + object return
|
||||||
|
fff: {
|
||||||
|
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
hhh: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
// default + type casting
|
||||||
|
ggg: {
|
||||||
|
type: String as PropType<'foo' | 'bar'>,
|
||||||
|
default: 'foo'
|
||||||
|
},
|
||||||
|
// default + function
|
||||||
|
ffff: {
|
||||||
|
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
|
||||||
|
default: (_a: number, _b: string) => ({ a: true })
|
||||||
|
},
|
||||||
|
validated: {
|
||||||
|
type: String,
|
||||||
|
// validator requires explicit annotation
|
||||||
|
validator: (val: unknown) => val !== ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
return {
|
||||||
|
setupA: 1,
|
||||||
|
setupB: ref(1),
|
||||||
|
setupC: {
|
||||||
|
a: ref(2)
|
||||||
|
},
|
||||||
|
setupProps: props
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { props, rawBindings, setup } = extractComponentOptions(MyComponent)
|
||||||
|
|
||||||
|
// props
|
||||||
|
expectType<ExpectedProps['a']>(props.a)
|
||||||
|
expectType<ExpectedProps['b']>(props.b)
|
||||||
|
expectType<ExpectedProps['e']>(props.e)
|
||||||
|
expectType<ExpectedProps['bb']>(props.bb)
|
||||||
|
expectType<ExpectedProps['bbb']>(props.bbb)
|
||||||
|
expectType<ExpectedProps['cc']>(props.cc)
|
||||||
|
expectType<ExpectedProps['dd']>(props.dd)
|
||||||
|
expectType<ExpectedProps['ee']>(props.ee)
|
||||||
|
expectType<ExpectedProps['ff']>(props.ff)
|
||||||
|
expectType<ExpectedProps['ccc']>(props.ccc)
|
||||||
|
expectType<ExpectedProps['ddd']>(props.ddd)
|
||||||
|
expectType<ExpectedProps['eee']>(props.eee)
|
||||||
|
expectType<ExpectedProps['fff']>(props.fff)
|
||||||
|
expectType<ExpectedProps['hhh']>(props.hhh)
|
||||||
|
expectType<ExpectedProps['ggg']>(props.ggg)
|
||||||
|
expectType<ExpectedProps['ffff']>(props.ffff)
|
||||||
|
expectType<ExpectedProps['validated']>(props.validated)
|
||||||
|
|
||||||
|
// raw bindings
|
||||||
|
expectType<Number>(rawBindings.setupA)
|
||||||
|
expectType<Ref<Number>>(rawBindings.setupB)
|
||||||
|
expectType<Ref<Number>>(rawBindings.setupC.a)
|
||||||
|
expectType<Number>(rawBindings.setupA)
|
||||||
|
|
||||||
|
// raw bindings props
|
||||||
|
expectType<ExpectedProps['a']>(rawBindings.setupProps.a)
|
||||||
|
expectType<ExpectedProps['b']>(rawBindings.setupProps.b)
|
||||||
|
expectType<ExpectedProps['e']>(rawBindings.setupProps.e)
|
||||||
|
expectType<ExpectedProps['bb']>(rawBindings.setupProps.bb)
|
||||||
|
expectType<ExpectedProps['bbb']>(rawBindings.setupProps.bbb)
|
||||||
|
expectType<ExpectedProps['cc']>(rawBindings.setupProps.cc)
|
||||||
|
expectType<ExpectedProps['dd']>(rawBindings.setupProps.dd)
|
||||||
|
expectType<ExpectedProps['ee']>(rawBindings.setupProps.ee)
|
||||||
|
expectType<ExpectedProps['ff']>(rawBindings.setupProps.ff)
|
||||||
|
expectType<ExpectedProps['ccc']>(rawBindings.setupProps.ccc)
|
||||||
|
expectType<ExpectedProps['ddd']>(rawBindings.setupProps.ddd)
|
||||||
|
expectType<ExpectedProps['eee']>(rawBindings.setupProps.eee)
|
||||||
|
expectType<ExpectedProps['fff']>(rawBindings.setupProps.fff)
|
||||||
|
expectType<ExpectedProps['hhh']>(rawBindings.setupProps.hhh)
|
||||||
|
expectType<ExpectedProps['ggg']>(rawBindings.setupProps.ggg)
|
||||||
|
expectType<ExpectedProps['ffff']>(rawBindings.setupProps.ffff)
|
||||||
|
expectType<ExpectedProps['validated']>(rawBindings.setupProps.validated)
|
||||||
|
|
||||||
|
// setup
|
||||||
|
expectType<Number>(setup.setupA)
|
||||||
|
expectType<Number>(setup.setupB)
|
||||||
|
expectType<Ref<Number>>(setup.setupC.a)
|
||||||
|
expectType<Number>(setup.setupA)
|
||||||
|
|
||||||
|
// raw bindings props
|
||||||
|
expectType<ExpectedProps['a']>(setup.setupProps.a)
|
||||||
|
expectType<ExpectedProps['b']>(setup.setupProps.b)
|
||||||
|
expectType<ExpectedProps['e']>(setup.setupProps.e)
|
||||||
|
expectType<ExpectedProps['bb']>(setup.setupProps.bb)
|
||||||
|
expectType<ExpectedProps['bbb']>(setup.setupProps.bbb)
|
||||||
|
expectType<ExpectedProps['cc']>(setup.setupProps.cc)
|
||||||
|
expectType<ExpectedProps['dd']>(setup.setupProps.dd)
|
||||||
|
expectType<ExpectedProps['ee']>(setup.setupProps.ee)
|
||||||
|
expectType<ExpectedProps['ff']>(setup.setupProps.ff)
|
||||||
|
expectType<ExpectedProps['ccc']>(setup.setupProps.ccc)
|
||||||
|
expectType<ExpectedProps['ddd']>(setup.setupProps.ddd)
|
||||||
|
expectType<ExpectedProps['eee']>(setup.setupProps.eee)
|
||||||
|
expectType<ExpectedProps['fff']>(setup.setupProps.fff)
|
||||||
|
expectType<ExpectedProps['hhh']>(setup.setupProps.hhh)
|
||||||
|
expectType<ExpectedProps['ggg']>(setup.setupProps.ggg)
|
||||||
|
expectType<ExpectedProps['ffff']>(setup.setupProps.ffff)
|
||||||
|
expectType<ExpectedProps['validated']>(setup.setupProps.validated)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('options', () => {
|
||||||
|
const MyComponent = {
|
||||||
|
props: {
|
||||||
|
a: Number,
|
||||||
|
// required should make property non-void
|
||||||
|
b: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
e: Function,
|
||||||
|
// default value should infer type and make it non-void
|
||||||
|
bb: {
|
||||||
|
default: 'hello'
|
||||||
|
},
|
||||||
|
bbb: {
|
||||||
|
// Note: default function value requires arrow syntax + explicit
|
||||||
|
// annotation
|
||||||
|
default: (props: any) => (props.bb as string) || 'foo'
|
||||||
|
},
|
||||||
|
// explicit type casting
|
||||||
|
cc: Array as PropType<string[]>,
|
||||||
|
// required + type casting
|
||||||
|
dd: {
|
||||||
|
type: Object as PropType<{ n: 1 }>,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
// return type
|
||||||
|
ee: Function as PropType<() => string>,
|
||||||
|
// arguments + object return
|
||||||
|
ff: Function as PropType<(a: number, b: string) => { a: boolean }>,
|
||||||
|
// explicit type casting with constructor
|
||||||
|
ccc: Array as () => string[],
|
||||||
|
// required + contructor type casting
|
||||||
|
ddd: {
|
||||||
|
type: Array as () => string[],
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
// required + object return
|
||||||
|
eee: {
|
||||||
|
type: Function as PropType<() => { a: string }>,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
// required + arguments + object return
|
||||||
|
fff: {
|
||||||
|
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
hhh: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
// default + type casting
|
||||||
|
ggg: {
|
||||||
|
type: String as PropType<'foo' | 'bar'>,
|
||||||
|
default: 'foo'
|
||||||
|
},
|
||||||
|
// default + function
|
||||||
|
ffff: {
|
||||||
|
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
|
||||||
|
default: (_a: number, _b: string) => ({ a: true })
|
||||||
|
},
|
||||||
|
validated: {
|
||||||
|
type: String,
|
||||||
|
// validator requires explicit annotation
|
||||||
|
validator: (val: unknown) => val !== ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
setupA: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as const
|
||||||
|
|
||||||
|
const { props, rawBindings, setup } = extractComponentOptions(MyComponent)
|
||||||
|
|
||||||
|
// props
|
||||||
|
expectType<ExpectedProps['a']>(props.a)
|
||||||
|
expectType<ExpectedProps['b']>(props.b)
|
||||||
|
expectType<ExpectedProps['e']>(props.e)
|
||||||
|
expectType<ExpectedProps['bb']>(props.bb)
|
||||||
|
expectType<ExpectedProps['bbb']>(props.bbb)
|
||||||
|
expectType<ExpectedProps['cc']>(props.cc)
|
||||||
|
expectType<ExpectedProps['dd']>(props.dd)
|
||||||
|
expectType<ExpectedProps['ee']>(props.ee)
|
||||||
|
expectType<ExpectedProps['ff']>(props.ff)
|
||||||
|
expectType<ExpectedProps['ccc']>(props.ccc)
|
||||||
|
expectType<ExpectedProps['ddd']>(props.ddd)
|
||||||
|
expectType<ExpectedProps['eee']>(props.eee)
|
||||||
|
expectType<ExpectedProps['fff']>(props.fff)
|
||||||
|
expectType<ExpectedProps['hhh']>(props.hhh)
|
||||||
|
expectType<ExpectedProps['ggg']>(props.ggg)
|
||||||
|
// expectType<ExpectedProps['ffff']>(props.ffff) // todo fix
|
||||||
|
expectType<ExpectedProps['validated']>(props.validated)
|
||||||
|
|
||||||
|
// rawBindings
|
||||||
|
expectType<Number>(rawBindings.setupA)
|
||||||
|
|
||||||
|
//setup
|
||||||
|
expectType<Number>(setup.setupA)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('array props', () => {
|
||||||
|
describe('defineComponent', () => {
|
||||||
|
const MyComponent = defineComponent({
|
||||||
|
props: ['a', 'b'],
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
c: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { props, rawBindings, setup } = extractComponentOptions(MyComponent)
|
||||||
|
|
||||||
|
// @ts-expect-error props should be readonly
|
||||||
|
expectError((props.a = 1))
|
||||||
|
expectType<any>(props.a)
|
||||||
|
expectType<any>(props.b)
|
||||||
|
|
||||||
|
expectType<number>(rawBindings.c)
|
||||||
|
expectType<number>(setup.c)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('options', () => {
|
||||||
|
const MyComponent = {
|
||||||
|
props: ['a', 'b'] as const,
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
c: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { props, rawBindings, setup } = extractComponentOptions(MyComponent)
|
||||||
|
|
||||||
|
// @ts-expect-error props should be readonly
|
||||||
|
expectError((props.a = 1))
|
||||||
|
|
||||||
|
// TODO infer the correct keys
|
||||||
|
// expectType<any>(props.a)
|
||||||
|
// expectType<any>(props.b)
|
||||||
|
|
||||||
|
expectType<number>(rawBindings.c)
|
||||||
|
expectType<number>(setup.c)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('no props', () => {
|
||||||
|
describe('defineComponent', () => {
|
||||||
|
const MyComponent = defineComponent({
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
setupA: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { rawBindings, setup } = extractComponentOptions(MyComponent)
|
||||||
|
|
||||||
|
expectType<number>(rawBindings.setupA)
|
||||||
|
expectType<number>(setup.setupA)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('options', () => {
|
||||||
|
const MyComponent = {
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
setupA: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rawBindings, setup } = extractComponentOptions(MyComponent)
|
||||||
|
|
||||||
|
expectType<number>(rawBindings.setupA)
|
||||||
|
expectType<number>(setup.setupA)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('functional', () => {
|
||||||
|
// TODO `props.foo` is `number|undefined`
|
||||||
|
// describe('defineComponent', () => {
|
||||||
|
// const MyComponent = defineComponent((props: { foo: number }) => {})
|
||||||
|
|
||||||
|
// const { props } = extractComponentOptions(MyComponent)
|
||||||
|
|
||||||
|
// expectType<number>(props.foo)
|
||||||
|
// })
|
||||||
|
|
||||||
|
describe('function', () => {
|
||||||
|
const MyComponent = (props: { foo: number }) => props.foo
|
||||||
|
const { props } = extractComponentOptions(MyComponent)
|
||||||
|
|
||||||
|
expectType<number>(props.foo)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('typed', () => {
|
||||||
|
const MyComponent: FunctionalComponent<{ foo: number }> = (_, _2) => {}
|
||||||
|
|
||||||
|
const { props } = extractComponentOptions(MyComponent)
|
||||||
|
|
||||||
|
expectType<number>(props.foo)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
declare type VueClass<Props = {}> = {
|
||||||
|
new (): ComponentPublicInstance<Props>
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('class', () => {
|
||||||
|
const MyComponent: VueClass<{ foo: number }> = {} as any
|
||||||
|
|
||||||
|
const { props } = extractComponentOptions(MyComponent)
|
||||||
|
|
||||||
|
expectType<number>(props.foo)
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user