types(runtime-core): refactor defineComponent (#1883)

This commit is contained in:
Carlos Rodrigues 2020-09-15 16:46:11 +01:00 committed by GitHub
parent 848ccf56fb
commit 4fd468aced
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 583 additions and 132 deletions

View File

@ -32,17 +32,17 @@ const readonlyGet = /*#__PURE__*/ createGetter(true)
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
}

View File

@ -6,22 +6,65 @@ import {
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
@ -35,21 +78,7 @@ export function defineComponent<Props, RawBindings = object>(
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)
@ -58,11 +87,11 @@ export function defineComponent<
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<
@ -76,30 +105,7 @@ export function defineComponent<
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 }
@ -126,32 +132,17 @@ export function defineComponent<
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
@ -179,33 +170,20 @@ export function defineComponent<
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
}
})

View File

@ -26,7 +26,12 @@ import { warn } from './warning'
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,
@ -118,13 +123,29 @@ export interface ClassComponent {
* 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 }

View File

@ -72,8 +72,6 @@ export interface ComponentCustomOptions {}
export type RenderFunction = () => VNodeChild
export type UnwrapAsyncBindings<T> = T extends Promise<infer S> ? S : T
export interface ComponentOptionsBase<
Props,
RawBindings,
@ -92,7 +90,7 @@ export interface ComponentOptionsBase<
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
@ -230,10 +228,29 @@ export type ComponentOptionsWithObjectProps<
>
>
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,
@ -638,17 +655,13 @@ export function applyOptions(
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))

View File

@ -27,8 +27,7 @@ import {
OptionTypesType,
OptionTypesKeys,
resolveMergedOptions,
isInBeforeCreate,
UnwrapAsyncBindings
isInBeforeCreate
} from './componentOptions'
import { EmitsOptions, EmitFn } from './componentEmits'
import { Slots } from './componentSlots'
@ -102,7 +101,18 @@ type UnwrapMixinsType<
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
@ -138,6 +148,7 @@ export type CreateComponentPublicInstance<
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<
@ -169,7 +180,7 @@ export type ComponentPublicInstance<
options?: WatchOptions
): WatchStopHandle
} & P &
ShallowUnwrapRef<UnwrapAsyncBindings<B>> &
ShallowUnwrapRef<B> &
D &
ExtractComputedReturns<C> &
M &

View File

@ -10,9 +10,9 @@ import { Teleport, TeleportProps } from './components/Teleport'
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.
@ -50,7 +50,7 @@ type RawProps = VNodeProps & {
__v_isVNode?: never
// used to differ from Array children
[Symbol.iterator]?: never
} & { [key: string]: any }
} & Record<string, any>
type RawChildren =
| string
@ -112,10 +112,17 @@ export function h<P, E extends EmitsOptions = {}>(
// 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
@ -127,6 +134,14 @@ export function h<P>(
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

View File

@ -41,7 +41,7 @@ export {
} 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 ----------------------------------------------------------------

View 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)
})