Skip to content

Commit

Permalink
fix: resolve conflicts with vue2 interface (#869)
Browse files Browse the repository at this point in the history
  • Loading branch information
MinatoHikari committed Dec 17, 2021
1 parent 73524d2 commit e84b488
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 8 deletions.
21 changes: 19 additions & 2 deletions src/component/componentProxy.ts
Expand Up @@ -19,6 +19,7 @@ import {
} from './componentOptions'
import {
ComponentInternalInstance,
ComponentRenderEmitFn,
EmitFn,
EmitsOptions,
ObjectEmitsOptions,
Expand Down Expand Up @@ -67,8 +68,24 @@ export type ComponentRenderProxy<
? Partial<Defaults> & Omit<P & PublicProps, keyof Defaults>
: P & PublicProps
>
$attrs: Data
$emit: EmitFn<Emits>
$attrs: Record<string, string>
$emit: ComponentRenderEmitFn<
Emits,
keyof Emits,
ComponentRenderProxy<
P,
B,
D,
C,
M,
Mixin,
Extends,
Emits,
PublicProps,
Defaults,
MakeDefaultsOptional
>
>
} & Readonly<P> &
ShallowUnwrapRef<B> &
D &
Expand Down
18 changes: 13 additions & 5 deletions src/runtimeContext.ts
Expand Up @@ -9,6 +9,7 @@ import {
UnionToIntersection,
isFunction,
} from './utils'
import Vue$1 from 'vue'

let vueDependency: VueConstructor | undefined = undefined

Expand Down Expand Up @@ -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<infer V>
? (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<Options, Event, T>

export type Slots = Readonly<InternalSlots>

export interface SetupContext<E = EmitsOptions> {
Expand Down
50 changes: 50 additions & 0 deletions 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<Record<string, string>>(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<V> = { new (...args: any[]): V & Vue } & typeof Vue

function useComponentRef<T extends VueClass<Vue>>() {
return ref<InstanceType<T>>(undefined!) as Ref<InstanceType<T>>
}

const foo = useComponentRef<typeof testComponent>()

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'))
})
3 changes: 2 additions & 1 deletion test-dts/tsconfig.vue3.json
Expand Up @@ -4,5 +4,6 @@
"paths": {
"@vue/composition-api": ["../node_modules/vue3/dist/vue.d.ts"]
}
}
},
"exclude": ["./defineComponent-vue2.d.tsx"]
}
1 change: 1 addition & 0 deletions tsconfig.json
Expand Up @@ -8,6 +8,7 @@
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"strict": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
Expand Down

0 comments on commit e84b488

Please sign in to comment.