const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
const arrayInstrumentations: Record<string, Function> = {}
-;['includes', 'indexOf', 'lastIndexOf'].forEach(key => {
- arrayInstrumentations[key] = function(...args: any[]): any {
- const arr = toRaw(this) as any
- for (let i = 0, l = (this as any).length; i < l; i++) {
+;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => {
+ arrayInstrumentations[key] = function(this: unknown[], ...args: unknown[]) {
+ const arr = toRaw(this)
+ for (let i = 0, l = this.length; i < l; i++) {
track(arr, TrackOpTypes.GET, i + '')
}
// 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 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 {
return res
}
ComponentOptionsWithObjectProps,
ComponentOptionsMixin,
RenderFunction,
- UnwrapAsyncBindings
+ ComponentOptionsBase
} from './componentOptions'
import {
SetupContext,
- FunctionalComponent,
AllowedComponentProps,
ComponentCustomProps
} from './component'
-import {
- CreateComponentPublicInstance,
- ComponentPublicInstanceConstructor
-} from './componentPublicInstance'
import { ExtractPropTypes, ComponentPropsOptions } from './componentProps'
import { EmitsOptions } from './componentEmits'
import { isFunction } from '@vue/shared'
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
// when declaring components. Type inference is provided in the component
props: Readonly<Props>,
ctx: SetupContext
) => RawBindings | RenderFunction
-): ComponentPublicInstanceConstructor<
- CreateComponentPublicInstance<
- Props,
- UnwrapAsyncBindings<RawBindings>,
- {},
- {},
- {},
- {},
- {},
- {},
- // public props
- VNodeProps & Props & AllowedComponentProps & ComponentCustomProps
- >
-> &
- FunctionalComponent<Props>
+): DefineComponent<Props, RawBindings>
// overload 2: object format with no props
// (uses user defined props interface)
Props = {},
RawBindings = {},
D = {},
- C extends ComputedOptions = {},
- M extends MethodOptions = {},
+ C extends ComputedOptions = ComputedOptions,
+ M extends MethodOptions = MethodOptions,
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
- E extends EmitsOptions = Record<string, any>,
+ E extends EmitsOptions = EmitsOptions,
EE extends string = string
>(
options: ComponentOptionsWithoutProps<
E,
EE
>
-): ComponentPublicInstanceConstructor<
- CreateComponentPublicInstance<
- Props,
- UnwrapAsyncBindings<RawBindings>,
- D,
- C,
- M,
- Mixin,
- Extends,
- E,
- VNodeProps & Props & AllowedComponentProps & ComponentCustomProps
- >
-> &
- ComponentOptionsWithoutProps<
- Props,
- RawBindings,
- D,
- C,
- M,
- Mixin,
- Extends,
- E,
- EE
- >
+): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE>
// overload 3: object format with array props declaration
// props inferred as { [key in PropNames]?: any }
E,
EE
>
-): ComponentPublicInstanceConstructor<
- // array props technically doesn't place any constraints on props in TSX before,
- // but now we can export array props in TSX
- CreateComponentPublicInstance<
- Readonly<{ [key in PropNames]?: any }>,
- UnwrapAsyncBindings<RawBindings>,
- D,
- C,
- M,
- Mixin,
- Extends,
- E,
- AllowedComponentProps & ComponentCustomProps
- >
-> &
- ComponentOptionsWithArrayProps<
- PropNames,
- RawBindings,
- D,
- C,
- M,
- Mixin,
- Extends,
- E,
- EE
- >
+): DefineComponent<
+ Readonly<{ [key in PropNames]?: any }>,
+ RawBindings,
+ D,
+ C,
+ M,
+ Mixin,
+ Extends,
+ E,
+ EE
+>
// overload 4: object format with object props declaration
// see `ExtractPropTypes` in ./componentProps.ts
E,
EE
>
-): ComponentPublicInstanceConstructor<
- 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
- >
+): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
// implementation, close to no-op
export function defineComponent(options: unknown) {
return isFunction(options) ? { setup: options, name: options.name } : options
}
+
+defineComponent({
+ async setup() {
+ return {
+ a: 123
+ }
+ },
+ render() {
+ this.a
+ }
+})
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
import { AppContext, createAppContext, AppConfig } from './apiCreateApp'
import { Directive, validateDirectiveName } from './directives'
-import { applyOptions, ComponentOptions } from './componentOptions'
+import {
+ applyOptions,
+ ComponentOptions,
+ ComputedOptions,
+ MethodOptions
+} from './componentOptions'
import {
EmitsOptions,
ObjectEmitsOptions,
* values, e.g. checking if its a function or not. This is mostly for internal
* 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.
* 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 type RenderFunction = () => VNodeChild
-export type UnwrapAsyncBindings<T> = T extends Promise<infer S> ? S : T
-
export interface ComponentOptionsBase<
Props,
RawBindings,
this: void,
props: Props,
ctx: SetupContext<E>
- ) => RawBindings | RenderFunction | void
+ ) => Promise<RawBindings> | RawBindings | RenderFunction | void
name?: string
template?: string | object // can be a direct DOM node
// Note: we are intentionally using the signature-less `Function` type here
>
>
-export type ComponentOptions =
- | ComponentOptionsWithoutProps<any, any, any, any, any>
- | ComponentOptionsWithObjectProps<any, any, any, any, any>
- | ComponentOptionsWithArrayProps<any, any, any, any, any>
+export type ComponentOptions<
+ Props = {},
+ RawBindings = 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<
any,
onRenderTriggered(renderTriggered.bind(publicThis))
}
if (__DEV__ && beforeDestroy) {
- warn(
- `\`beforeDestroy\` has been renamed to \`beforeUnmount\`.`
- )
+ warn(`\`beforeDestroy\` has been renamed to \`beforeUnmount\`.`)
}
if (beforeUnmount) {
onBeforeUnmount(beforeUnmount.bind(publicThis))
}
if (__DEV__ && destroyed) {
- warn(
- `\`destroyed\` has been renamed to \`unmounted\`.`
- )
+ warn(`\`destroyed\` has been renamed to \`unmounted\`.`)
}
if (unmounted) {
onUnmounted(unmounted.bind(publicThis))
OptionTypesType,
OptionTypesKeys,
resolveMergedOptions,
- isInBeforeCreate,
- UnwrapAsyncBindings
+ isInBeforeCreate
} from './componentOptions'
import { EmitsOptions, EmitFn } from './componentEmits'
import { Slots } from './componentSlots'
type EnsureNonVoid<T> = T extends void ? {} : T
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
__isTeleport?: never
PublicProps,
ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E>
>
+
// public properties exposed on the proxy, which is used as the render context
// in templates (as `this` in the render option)
export type ComponentPublicInstance<
options?: WatchOptions
): WatchStopHandle
} & P &
- ShallowUnwrapRef<UnwrapAsyncBindings<B>> &
+ ShallowUnwrapRef<B> &
D &
ExtractComputedReturns<C> &
M &
import { Suspense, SuspenseProps } from './components/Suspense'
import { isObject, isArray } from '@vue/shared'
import { RawSlots } from './componentSlots'
-import { FunctionalComponent, Component } from './component'
-import { ComponentOptions } from './componentOptions'
+import { FunctionalComponent, Component, ComponentOptions } from './component'
import { EmitsOptions } from './componentEmits'
+import { DefineComponent } from './apiDefineComponent'
// `h` is a more user-friendly version of `createVNode` that allows omitting the
// props when possible. It is intended for manually written render functions.
__v_isVNode?: never
// used to differ from Array children
[Symbol.iterator]?: never
-} & { [key: string]: any }
+} & Record<string, any>
type RawChildren =
| string
// catch-all for generic component types
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
-export function h<T extends ComponentOptions | FunctionalComponent<{}>>(
- type: T,
- props?: RawProps | null,
+export function h<P>(
+ type: ComponentOptions<P>,
+ props?: (RawProps & P) | ({} extends P ? null : never),
children?: RawChildren | RawSlots
): VNode
children?: RawChildren | RawSlots
): 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
export function h(type: any, propsOrChildren?: any, children?: any): VNode {
const l = arguments.length
} from './apiLifecycle'
export { provide, inject } from './apiInject'
export { nextTick } from './scheduler'
-export { defineComponent } from './apiDefineComponent'
+export { defineComponent, DefineComponent } from './apiDefineComponent'
export { defineAsyncComponent } from './apiAsyncComponent'
// Advanced API ----------------------------------------------------------------
--- /dev/null
+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)
+})