diff --git a/types/test/v3/define-component-test.tsx b/types/test/v3/define-component-test.tsx index 7e6d1968ca..0dfb162f5c 100644 --- a/types/test/v3/define-component-test.tsx +++ b/types/test/v3/define-component-test.tsx @@ -966,6 +966,47 @@ describe('emits', () => { } } }) + + // should have `onXXX` props for emits + const a = defineComponent({ + props: { + bar: String + }, + emits: { + foo: (n: number) => n > 0 + }, + setup(props) { + expectType<((n: number) => boolean) | undefined>(props.onFoo) + } + }) + + const b = defineComponent({ + extends: a, + props: { + bar2: String + }, + emits: { + foo2: (n: number) => n > 0 + }, + setup(props) { + expectType<((n: number) => boolean) | undefined>(props.onFoo) + } + }) + + defineComponent({ + mixins: [a, b], + props: { + bar3: String + }, + emits: { + foo3: (n: number) => n > 0 + }, + setup(props) { + expectType<((n: number) => boolean) | undefined>(props.onFoo) + expectType<((n: number) => boolean) | undefined>(props.onFoo2) + expectType<((n: number) => boolean) | undefined>(props.onFoo3) + } + }) }) // describe('componentOptions setup should be `SetupContext`', () => { diff --git a/types/v3-component-options.d.ts b/types/v3-component-options.d.ts index e2da34e753..7a6416727a 100644 --- a/types/v3-component-options.d.ts +++ b/types/v3-component-options.d.ts @@ -1,7 +1,7 @@ import { Vue } from './vue' import { VNode } from './vnode' import { ComponentOptions as Vue2ComponentOptions } from './options' -import { EmitsOptions, SetupContext } from './v3-setup-context' +import { EmitsOptions, EmitsToProps, SetupContext } from './v3-setup-context' import { Data, LooseRequired, UnionToIntersection } from './common' import { ComponentPropsOptions, @@ -52,7 +52,7 @@ export type SetupFunction< Emits extends EmitsOptions = {} > = ( this: void, - props: Readonly, + props: Readonly>, ctx: SetupContext ) => RawBindings | (() => VNode | null) | void diff --git a/types/v3-define-component.d.ts b/types/v3-define-component.d.ts index 03ef52d185..c21bb4a7a6 100644 --- a/types/v3-define-component.d.ts +++ b/types/v3-define-component.d.ts @@ -17,7 +17,7 @@ import { CreateComponentPublicInstance } from './v3-component-public-instance' import { Data, HasDefined } from './common' -import { EmitsOptions } from './v3-setup-context' +import { EmitsOptions, EmitsToProps } from './v3-setup-context' import { CreateElement, RenderContext } from './umd' export type DefineComponent< @@ -31,9 +31,9 @@ export type DefineComponent< E extends EmitsOptions = {}, EE extends string = string, Props = Readonly< - PropsOrPropOptions extends ComponentPropsOptions + (PropsOrPropOptions extends ComponentPropsOptions ? ExtractPropTypes - : PropsOrPropOptions + : PropsOrPropOptions) & EmitsToProps >, Defaults = ExtractDefaultPropTypes > = ComponentPublicInstanceConstructor< diff --git a/types/v3-setup-context.d.ts b/types/v3-setup-context.d.ts index 77b49bed8a..f6bee6431b 100644 --- a/types/v3-setup-context.d.ts +++ b/types/v3-setup-context.d.ts @@ -13,6 +13,25 @@ export type ObjectEmitsOptions = Record< export type EmitsOptions = ObjectEmitsOptions | string[] +export type EmitsToProps = T extends string[] + ? { + [K in string & `on${Capitalize}`]?: (...args: any[]) => any + } + : T extends ObjectEmitsOptions + ? { + [K in string & + `on${Capitalize}`]?: K extends `on${infer C}` + ? T[Uncapitalize] extends null + ? (...args: any[]) => any + : ( + ...args: T[Uncapitalize] extends (...args: infer P) => any + ? P + : never + ) => any + : never + } + : {} + export type EmitFn< Options = ObjectEmitsOptions, Event extends keyof Options = keyof Options,