diff --git a/packages/runtime-core/src/apiAsyncComponent.ts b/packages/runtime-core/src/apiAsyncComponent.ts index 8825eefa..dc92b90d 100644 --- a/packages/runtime-core/src/apiAsyncComponent.ts +++ b/packages/runtime-core/src/apiAsyncComponent.ts @@ -111,7 +111,7 @@ export function defineAsyncComponent< ) } - return defineComponent({ + return defineComponent<{}>({ name: 'AsyncComponentWrapper', __asyncLoader: load, diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index 83446346..f0f69af3 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -6,7 +6,8 @@ import { ComponentOptionsWithObjectProps, ComponentOptionsMixin, RenderFunction, - ComponentOptionsBase + ComponentOptionsBase, + ComponentProvideOptions } from './componentOptions' import { SetupContext, @@ -40,6 +41,8 @@ export type DefineComponent< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, EE extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, + RawOptions extends {} = {}, PP = PublicProps, Props = Readonly< PropsOrPropOptions extends ComponentPropsOptions @@ -48,22 +51,23 @@ export type DefineComponent< > & ({} extends E ? {} : EmitsToProps), Defaults = ExtractDefaultPropTypes -> = ComponentPublicInstanceConstructor< - CreateComponentPublicInstance< - Props, - RawBindings, - D, - C, - M, - Mixin, - Extends, - E, - PP & Props, - Defaults, - true +> = RawOptions & + ComponentPublicInstanceConstructor< + CreateComponentPublicInstance< + Props, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + PP & Props, + Defaults, + true + > & + Props > & - Props -> & ComponentOptionsBase< Props, RawBindings, @@ -74,7 +78,8 @@ export type DefineComponent< Extends, E, EE, - Defaults + Defaults, + Provide > & PP @@ -104,20 +109,36 @@ export function defineComponent< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, - EE extends string = string + EE extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, + Options extends {} = {} >( - options: ComponentOptionsWithoutProps< - Props, - RawBindings, - D, - C, - M, - Mixin, - Extends, - E, - EE - > -): DefineComponent + options: Options & + ComponentOptionsWithoutProps< + Props, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + EE, + Provide + > +): DefineComponent< + Props, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + EE, + Provide, + Options +> // overload 3: object format with array props declaration // props inferred as { [key in PropNames]?: any } @@ -131,19 +152,23 @@ export function defineComponent< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = Record, - EE extends string = string + EE extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, + Options extends {} = {} >( - options: ComponentOptionsWithArrayProps< - PropNames, - RawBindings, - D, - C, - M, - Mixin, - Extends, - E, - EE - > + options: Options & + ComponentOptionsWithArrayProps< + PropNames, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + EE, + Provide + > ): DefineComponent< Readonly<{ [key in PropNames]?: any }>, RawBindings, @@ -153,7 +178,9 @@ export function defineComponent< Mixin, Extends, E, - EE + EE, + Provide, + Options > // overload 4: object format with object props declaration @@ -169,20 +196,36 @@ export function defineComponent< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = Record, - EE extends string = string + EE extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, + Options extends {} = {} >( - options: ComponentOptionsWithObjectProps< - PropsOptions, - RawBindings, - D, - C, - M, - Mixin, - Extends, - E, - EE - > -): DefineComponent + options: Options & + ComponentOptionsWithObjectProps< + PropsOptions, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + EE, + Provide + > +): DefineComponent< + PropsOptions, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + EE, + Provide, + Options +> // implementation, close to no-op export function defineComponent(options: unknown) { diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index e8fa676a..b4fd4790 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -117,8 +117,9 @@ export interface ComponentOptionsBase< Extends extends ComponentOptionsMixin, E extends EmitsOptions, EE extends string = string, - Defaults = {} -> extends LegacyOptions, + Defaults = {}, + Provide extends ComponentProvideOptions = ComponentProvideOptions +> extends LegacyOptions, ComponentInternalOptions, ComponentCustomOptions { setup?: ( @@ -224,6 +225,7 @@ export type ComponentOptionsWithoutProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, PE = Props & EmitsToProps > = ComponentOptionsBase< PE, @@ -235,7 +237,8 @@ export type ComponentOptionsWithoutProps< Extends, E, EE, - {} + {}, + Provide > & { props?: undefined } & ThisType< @@ -252,6 +255,7 @@ export type ComponentOptionsWithArrayProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, Props = Readonly<{ [key in PropNames]?: any }> & EmitsToProps > = ComponentOptionsBase< Props, @@ -263,7 +267,8 @@ export type ComponentOptionsWithArrayProps< Extends, E, EE, - {} + {}, + Provide > & { props: PropNames[] } & ThisType< @@ -289,6 +294,7 @@ export type ComponentOptionsWithObjectProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, + Provide extends ComponentProvideOptions = ComponentProvideOptions, Props = Readonly> & EmitsToProps, Defaults = ExtractDefaultPropTypes > = ComponentOptionsBase< @@ -301,7 +307,8 @@ export type ComponentOptionsWithObjectProps< Extends, E, EE, - Defaults + Defaults, + Provide > & { props: PropsOptions & ThisType } & ThisType< @@ -384,6 +391,10 @@ type ComponentWatchOptionItem = WatchOptionItem | WatchOptionItem[] type ComponentWatchOptions = Record +export type ComponentProvideOptions = ObjectProvideOptions | Function + +type ObjectProvideOptions = Record + type ComponentInjectOptions = string[] | ObjectInjectOptions type ObjectInjectOptions = Record< @@ -397,7 +408,8 @@ interface LegacyOptions< C extends ComputedOptions, M extends MethodOptions, Mixin extends ComponentOptionsMixin, - Extends extends ComponentOptionsMixin + Extends extends ComponentOptionsMixin, + Provide extends ComponentProvideOptions = ComponentProvideOptions > { compatConfig?: CompatConfig @@ -431,7 +443,7 @@ interface LegacyOptions< computed?: C methods?: M watch?: ComponentWatchOptions - provide?: Data | Function + provide?: Provide inject?: ComponentInjectOptions // assets diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index 03ee0e0e..faae5312 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -34,7 +34,8 @@ import { OptionTypesKeys, resolveMergedOptions, shouldCacheAccess, - MergedComponentOptionsOverride + MergedComponentOptionsOverride, + ComponentProvideOptions } from './componentOptions' import { EmitsOptions, EmitFn } from './componentEmits' import { Slots } from './componentSlots' @@ -150,7 +151,8 @@ export type CreateComponentPublicInstance< PublicM extends MethodOptions = UnwrapMixinsType & EnsureNonVoid, PublicDefaults = UnwrapMixinsType & - EnsureNonVoid + EnsureNonVoid, + Provide extends ComponentProvideOptions = ComponentProvideOptions > = ComponentPublicInstance< PublicP, PublicB, @@ -161,7 +163,19 @@ export type CreateComponentPublicInstance< PublicProps, PublicDefaults, MakeDefaultsOptional, - ComponentOptionsBase + ComponentOptionsBase< + P, + B, + D, + C, + M, + Mixin, + Extends, + E, + string, + Defaults, + Provide + > > // public properties exposed on the proxy, which is used as the render context diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx index 031b4a13..3c4e9cb3 100644 --- a/test-dts/defineComponent.test-d.tsx +++ b/test-dts/defineComponent.test-d.tsx @@ -49,96 +49,98 @@ describe('with object props', () => { type GT = string & { __brand: unknown } - const MyComponent = defineComponent({ - props: { - a: Number, - // required should make property non-void - b: { - type: String, - required: true - }, - e: Function, - h: Boolean, - j: Function as PropType string | undefined)>, - // 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' - }, - bbbb: { - type: String, - default: undefined - }, - bbbbb: { - type: String, - default: () => undefined - }, - // explicit type casting - cc: Array as PropType, - // 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 + constructor 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: a > +b }) - }, - // union + function with different return types - iii: Function as PropType<(() => string) | (() => number)>, - // union + function with different args & same return type - jjj: { - type: Function as PropType< - ((arg1: string) => string) | ((arg1: string, arg2: string) => string) - >, - required: true - }, - kkk: null, - validated: { - type: String, - // validator requires explicit annotation - validator: (val: unknown) => val !== '' - }, - date: Date, - l: [Date], - ll: [Date, Number], - lll: [String, Number] + const props = { + a: Number, + // required should make property non-void + b: { + type: String, + required: true as true }, + e: Function, + h: Boolean, + j: Function as PropType string | undefined)>, + // 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' + }, + bbbb: { + type: String, + default: undefined + }, + bbbbb: { + type: String, + default: () => undefined + }, + // explicit type casting + cc: Array as PropType, + // required + type casting + dd: { + type: Object as PropType<{ n: 1 }>, + required: true as 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 + constructor type casting + ddd: { + type: Array as () => string[], + required: true as true + }, + // required + object return + eee: { + type: Function as PropType<() => { a: string }>, + required: true as true + }, + // required + arguments + object return + fff: { + type: Function as PropType<(a: number, b: string) => { a: boolean }>, + required: true as true + }, + hhh: { + type: Boolean, + required: true as 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: a > +b }) + }, + // union + function with different return types + iii: Function as PropType<(() => string) | (() => number)>, + // union + function with different args & same return type + jjj: { + type: Function as PropType< + ((arg1: string) => string) | ((arg1: string, arg2: string) => string) + >, + required: true as true + }, + kkk: null, + validated: { + type: String, + // validator requires explicit annotation + validator: (val: unknown) => val !== '' + }, + date: Date, + l: [Date], + ll: [Date, Number], + lll: [String, Number] + } + + const MyComponent = defineComponent({ + props, setup(props) { // type assertion. See https://github.com/SamVerschueren/tsd expectType(props.a) @@ -188,6 +190,9 @@ describe('with object props', () => { }) } }, + provide() { + return {} + }, render() { const props = this.$props expectType(props.a) @@ -258,6 +263,18 @@ describe('with object props', () => { expectType(MyComponent) + expectType(MyComponent.props) + // @ts-expect-error it should be an object and not any + expectError<[]>(MyComponent.props) + + expectType<() => {}>(MyComponent.provide) + // @ts-expect-error + expectError<[]>(MyComponent.provide) + + expectType<() => null>(MyComponent.render) + + expectType(defineComponent({}).render) + // Test TSX expectType(