Skip to content

Commit e84b488

Browse files
authored
fix: resolve conflicts with vue2 interface (#869)
1 parent 73524d2 commit e84b488

File tree

5 files changed

+85
-8
lines changed

5 files changed

+85
-8
lines changed

src/component/componentProxy.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from './componentOptions'
2020
import {
2121
ComponentInternalInstance,
22+
ComponentRenderEmitFn,
2223
EmitFn,
2324
EmitsOptions,
2425
ObjectEmitsOptions,
@@ -67,8 +68,24 @@ export type ComponentRenderProxy<
6768
? Partial<Defaults> & Omit<P & PublicProps, keyof Defaults>
6869
: P & PublicProps
6970
>
70-
$attrs: Data
71-
$emit: EmitFn<Emits>
71+
$attrs: Record<string, string>
72+
$emit: ComponentRenderEmitFn<
73+
Emits,
74+
keyof Emits,
75+
ComponentRenderProxy<
76+
P,
77+
B,
78+
D,
79+
C,
80+
M,
81+
Mixin,
82+
Extends,
83+
Emits,
84+
PublicProps,
85+
Defaults,
86+
MakeDefaultsOptional
87+
>
88+
>
7289
} & Readonly<P> &
7390
ShallowUnwrapRef<B> &
7491
D &

src/runtimeContext.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
UnionToIntersection,
1010
isFunction,
1111
} from './utils'
12+
import Vue$1 from 'vue'
1213

1314
let vueDependency: VueConstructor | undefined = undefined
1415

@@ -121,19 +122,26 @@ export type EmitsOptions = ObjectEmitsOptions | string[]
121122

122123
export type EmitFn<
123124
Options = ObjectEmitsOptions,
124-
Event extends keyof Options = keyof Options
125+
Event extends keyof Options = keyof Options,
126+
ReturnType extends void | Vue$1 = void
125127
> = Options extends Array<infer V>
126-
? (event: V, ...args: any[]) => void
128+
? (event: V, ...args: any[]) => ReturnType
127129
: {} extends Options // if the emit is empty object (usually the default value for emit) should be converted to function
128-
? (event: string, ...args: any[]) => void
130+
? (event: string, ...args: any[]) => ReturnType
129131
: UnionToIntersection<
130132
{
131133
[key in Event]: Options[key] extends (...args: infer Args) => any
132-
? (event: key, ...args: Args) => void
133-
: (event: key, ...args: any[]) => void
134+
? (event: key, ...args: Args) => ReturnType
135+
: (event: key, ...args: any[]) => ReturnType
134136
}[Event]
135137
>
136138

139+
export type ComponentRenderEmitFn<
140+
Options = ObjectEmitsOptions,
141+
Event extends keyof Options = keyof Options,
142+
T extends Vue$1 | void = void
143+
> = EmitFn<Options, Event, T>
144+
137145
export type Slots = Readonly<InternalSlots>
138146

139147
export interface SetupContext<E = EmitsOptions> {

test-dts/defineComponent-vue2.d.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import {
2+
defineComponent,
3+
describe,
4+
expectError,
5+
expectType,
6+
Ref,
7+
ref,
8+
} from './index'
9+
import Vue from 'vue'
10+
11+
describe('emits', () => {
12+
const testComponent = defineComponent({
13+
emits: {
14+
click: (n: number) => typeof n === 'number',
15+
input: (b: string) => b.length > 1,
16+
},
17+
created() {
18+
this.$emit('click', 1)
19+
this.$emit('click', 1).$emit('click', 1)
20+
this.$emit('input', 'foo')
21+
this.$emit('input', 'foo').$emit('click', 1)
22+
expectType<Record<string, string>>(this.$attrs)
23+
// @ts-expect-error
24+
expectError(this.$emit('input', 1).$emit('nope'))
25+
},
26+
})
27+
28+
// interface of vue2's $emit has no generics, notice that untyped types will be "event: string, ...args: any[]) => this" when using vue-class-component.
29+
// but we can get correct type when we use correct params
30+
// maybe we need vue 2.7 to fully support emit type
31+
type VueClass<V> = { new (...args: any[]): V & Vue } & typeof Vue
32+
33+
function useComponentRef<T extends VueClass<Vue>>() {
34+
return ref<InstanceType<T>>(undefined!) as Ref<InstanceType<T>>
35+
}
36+
37+
const foo = useComponentRef<typeof testComponent>()
38+
39+
foo.value.$emit('click', 1)
40+
foo.value.$emit('input', 'foo')
41+
foo.value.$emit('click', 1).$emit('click', 1)
42+
// @ts-expect-error
43+
expectError(foo.value.$emit('blah').$emit('click', 1))
44+
// @ts-expect-error
45+
expectError(foo.value.$emit('click').$emit('click', 1))
46+
// @ts-expect-error
47+
expectError(foo.value.$emit('blah').$emit('click', 1))
48+
// @ts-expect-error
49+
expectError(foo.value.$emit('blah'))
50+
})

test-dts/tsconfig.vue3.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"paths": {
55
"@vue/composition-api": ["../node_modules/vue3/dist/vue.d.ts"]
66
}
7-
}
7+
},
8+
"exclude": ["./defineComponent-vue2.d.tsx"]
89
}

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"moduleResolution": "node",
99
"skipLibCheck": true,
1010
"esModuleInterop": true,
11+
"experimentalDecorators": true,
1112
"strict": true,
1213
"strictNullChecks": true,
1314
"strictFunctionTypes": true,

0 commit comments

Comments
 (0)