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 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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
})
|
||||
|
@ -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 }
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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 &
|
||||
|
@ -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
|
||||
|
@ -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 ----------------------------------------------------------------
|
||||
|
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