& Omit
: P & PublicProps
>
- $attrs: Data
- $emit: EmitFn
+ $attrs: Record
+ $emit: ComponentRenderEmitFn<
+ Emits,
+ keyof Emits,
+ ComponentRenderProxy<
+ P,
+ B,
+ D,
+ C,
+ M,
+ Mixin,
+ Extends,
+ Emits,
+ PublicProps,
+ Defaults,
+ MakeDefaultsOptional
+ >
+ >
} & Readonly &
ShallowUnwrapRef &
D &
diff --git a/src/component/index.ts b/src/component/index.ts
index a54967f3..99a9f45e 100644
--- a/src/component/index.ts
+++ b/src/component/index.ts
@@ -25,4 +25,5 @@ export {
DirectiveHook,
ObjectDirective,
FunctionDirective,
+ Directive,
} from './directives'
diff --git a/src/runtimeContext.ts b/src/runtimeContext.ts
index 15c8eb3b..3df241ff 100644
--- a/src/runtimeContext.ts
+++ b/src/runtimeContext.ts
@@ -9,6 +9,7 @@ import {
UnionToIntersection,
isFunction,
} from './utils'
+import Vue$1 from 'vue'
let vueDependency: VueConstructor | undefined = undefined
@@ -121,19 +122,26 @@ export type EmitsOptions = ObjectEmitsOptions | string[]
export type EmitFn<
Options = ObjectEmitsOptions,
- Event extends keyof Options = keyof Options
+ Event extends keyof Options = keyof Options,
+ ReturnType extends void | Vue$1 = void
> = Options extends Array
- ? (event: V, ...args: any[]) => void
+ ? (event: V, ...args: any[]) => ReturnType
: {} extends Options // if the emit is empty object (usually the default value for emit) should be converted to function
- ? (event: string, ...args: any[]) => void
+ ? (event: string, ...args: any[]) => ReturnType
: UnionToIntersection<
{
[key in Event]: Options[key] extends (...args: infer Args) => any
- ? (event: key, ...args: Args) => void
- : (event: key, ...args: any[]) => void
+ ? (event: key, ...args: Args) => ReturnType
+ : (event: key, ...args: any[]) => ReturnType
}[Event]
>
+export type ComponentRenderEmitFn<
+ Options = ObjectEmitsOptions,
+ Event extends keyof Options = keyof Options,
+ T extends Vue$1 | void = void
+> = EmitFn
+
export type Slots = Readonly
export interface SetupContext {
diff --git a/test-dts/defineComponent-vue2.d.tsx b/test-dts/defineComponent-vue2.d.tsx
new file mode 100644
index 00000000..bb27617f
--- /dev/null
+++ b/test-dts/defineComponent-vue2.d.tsx
@@ -0,0 +1,50 @@
+import {
+ defineComponent,
+ describe,
+ expectError,
+ expectType,
+ Ref,
+ ref,
+} from './index'
+import Vue from 'vue'
+
+describe('emits', () => {
+ const testComponent = defineComponent({
+ emits: {
+ click: (n: number) => typeof n === 'number',
+ input: (b: string) => b.length > 1,
+ },
+ created() {
+ this.$emit('click', 1)
+ this.$emit('click', 1).$emit('click', 1)
+ this.$emit('input', 'foo')
+ this.$emit('input', 'foo').$emit('click', 1)
+ expectType>(this.$attrs)
+ // @ts-expect-error
+ expectError(this.$emit('input', 1).$emit('nope'))
+ },
+ })
+
+ // interface of vue2's $emit has no generics, notice that untyped types will be "event: string, ...args: any[]) => this" when using vue-class-component.
+ // but we can get correct type when we use correct params
+ // maybe we need vue 2.7 to fully support emit type
+ type VueClass = { new (...args: any[]): V & Vue } & typeof Vue
+
+ function useComponentRef>() {
+ return ref>(undefined!) as Ref>
+ }
+
+ const foo = useComponentRef()
+
+ foo.value.$emit('click', 1)
+ foo.value.$emit('input', 'foo')
+ foo.value.$emit('click', 1).$emit('click', 1)
+ // @ts-expect-error
+ expectError(foo.value.$emit('blah').$emit('click', 1))
+ // @ts-expect-error
+ expectError(foo.value.$emit('click').$emit('click', 1))
+ // @ts-expect-error
+ expectError(foo.value.$emit('blah').$emit('click', 1))
+ // @ts-expect-error
+ expectError(foo.value.$emit('blah'))
+})
diff --git a/test-dts/tsconfig.vue3.json b/test-dts/tsconfig.vue3.json
index 18344ced..12e21409 100644
--- a/test-dts/tsconfig.vue3.json
+++ b/test-dts/tsconfig.vue3.json
@@ -4,5 +4,6 @@
"paths": {
"@vue/composition-api": ["../node_modules/vue3/dist/vue.d.ts"]
}
- }
+ },
+ "exclude": ["./defineComponent-vue2.d.tsx"]
}
diff --git a/tsconfig.json b/tsconfig.json
index 2ad05247..bf2a457b 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -8,6 +8,7 @@
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
+ "experimentalDecorators": true,
"strict": true,
"strictNullChecks": true,
"strictFunctionTypes": true,