From e84b48837eadc756667a1caacb3afc3b7b8f415a Mon Sep 17 00:00:00 2001 From: MinatoHikari <35342316+MinatoHikari@users.noreply.github.com> Date: Sat, 18 Dec 2021 06:59:22 +0800 Subject: [PATCH] fix: resolve conflicts with vue2 interface (#869) --- src/component/componentProxy.ts | 21 ++++++++++-- src/runtimeContext.ts | 18 ++++++++--- test-dts/defineComponent-vue2.d.tsx | 50 +++++++++++++++++++++++++++++ test-dts/tsconfig.vue3.json | 3 +- tsconfig.json | 1 + 5 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 test-dts/defineComponent-vue2.d.tsx diff --git a/src/component/componentProxy.ts b/src/component/componentProxy.ts index 39202732..5199e2c6 100644 --- a/src/component/componentProxy.ts +++ b/src/component/componentProxy.ts @@ -19,6 +19,7 @@ import { } from './componentOptions' import { ComponentInternalInstance, + ComponentRenderEmitFn, EmitFn, EmitsOptions, ObjectEmitsOptions, @@ -67,8 +68,24 @@ export type ComponentRenderProxy< ? Partial & 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/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,