Skip to content

Commit

Permalink
fix: event type incorrect with prop format onFoo-bar
Browse files Browse the repository at this point in the history
close #1023
  • Loading branch information
johnsoncodehk committed Apr 20, 2022
1 parent 615eb15 commit 6dc54dd
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 51 deletions.
73 changes: 32 additions & 41 deletions packages/vue-code-gen/src/generators/template.ts
Expand Up @@ -86,7 +86,6 @@ export function generate(
slotsComponent: string,
emit: string,
slots: string,
events: Record<string, string>,
offsets: number[],
}> = {};
const localVars: Record<string, number> = {};
Expand All @@ -110,7 +109,6 @@ export function generate(
const var_slotsComponent = `__VLS_${elementIndex++}`;
const var_emit = `__VLS_${elementIndex++}`;
const var_slots = `__VLS_${elementIndex++}`;
const var_events: Record<string, string> = {};

if (isNamespacedTag) {
for (let i = 0; i < tagRanges.length; i++) {
Expand Down Expand Up @@ -141,26 +139,6 @@ export function generate(
tsCodeGen.addText(`declare const ${var_emit}: __VLS_types.ExtractEmit2<typeof ${var_rawComponent}>;\n`);
tsCodeGen.addText(`declare const ${var_slots}: __VLS_types.DefaultSlots<typeof ${var_rawComponent}>;\n`);

for (const eventName in tag.events) {

const var_on = `__VLS_${elementIndex++}`;
const key_1 = eventName; // click-outside
const key_3 = camelize(key_1); // clickOutside

tsCodeGen.addText(`type ${var_on} = \n`);
if (key_1 !== key_3) {
tsCodeGen.addText(`__VLS_types.FirstFunction<\n`);
tsCodeGen.addText(`__VLS_types.EmitEvent<typeof ${var_rawComponent}, '${key_1}'>,\n`);
tsCodeGen.addText(`__VLS_types.EmitEvent<typeof ${var_rawComponent}, '${key_3}'>\n`);
tsCodeGen.addText(`>;\n`);
}
else {
tsCodeGen.addText(`__VLS_types.EmitEvent<typeof ${var_rawComponent}, '${key_1}'>;\n`);
}

var_events[eventName] = var_on;
}

const name1 = tagName; // hello-world
const name2 = camelize(tagName); // helloWorld
const name3 = name2[0].toUpperCase() + name2.slice(1); // HelloWorld
Expand Down Expand Up @@ -232,7 +210,6 @@ export function generate(
slotsComponent: var_slotsComponent,
emit: var_emit,
slots: var_slots,
events: var_events,
offsets: tag.offsets.map(offset => htmlToTemplate(offset, offset)?.start).filter(notEmpty),
};
}
Expand Down Expand Up @@ -625,16 +602,21 @@ export function generate(

tryWriteInstance();

const varInstanceProps = `__VLS_${elementIndex++}`;

tsCodeGen.addText(`type ${varInstanceProps} = typeof ${varComponentInstance} extends { $props: infer Props } ? Props & Omit<__VLS_types.GlobalAttrs, keyof Props> & Record<string, unknown> : typeof ${tagResolves[node.tag].rawComponent} & Record<string, unknown>;\n`);
tsCodeGen.addText(`const __VLS_${elementIndex++}: {\n`);
tsCodeGen.addText(`'${prop.arg.loc.source}': __VLS_types.FillingEventArg<\n`);
{
tsCodeGen.addText(`__VLS_types.FirstFunction<\n`);
{
tsCodeGen.addText(`__VLS_types.FirstFunction<\n`);
tsCodeGen.addText(`${tagResolves[node.tag].events[prop.arg.loc.source]},\n`);
tsCodeGen.addText(`__VLS_types.EmitEvent<typeof ${tagResolves[node.tag].rawComponent}, '${prop.arg.loc.source}'>,\n`);
{
tsCodeGen.addText(`(typeof ${varComponentInstance} extends { $props: infer Props } ? Props & Omit<__VLS_types.GlobalAttrs, keyof Props> & Record<string, unknown> : typeof ${tagResolves[node.tag].rawComponent} & Record<string, unknown>)[`);

const key_2 = camelize('on-' + prop.arg.loc.source); // onClickOutside
const key_3 = 'on' + prop.arg.loc.source[0].toUpperCase() + prop.arg.loc.source.substring(1); // onClick-outside

tsCodeGen.addText(`${varInstanceProps}[`);
writeCodeWithQuotes(
key_2,
[{ start: prop.arg.loc.start.offset, end: prop.arg.loc.end.offset }],
Expand All @@ -653,23 +635,32 @@ export function generate(
},
},
);
tsCodeGen.addText(`]\n`);
}
tsCodeGen.addText(`>,\n`);

const camelizeName = camelize(prop.arg.loc.source);

if (camelizeName === prop.arg.loc.source) {
tsCodeGen.addText(`typeof ${varComponentInstance} extends { $emit: infer Emit } ? __VLS_types.EmitEvent2<Emit, '${prop.arg.loc.source}'> : unknown,\n`);
}
else {
tsCodeGen.addText(`__VLS_types.FirstFunction<\n`);
{
tsCodeGen.addText(`typeof ${varComponentInstance} extends { $emit: infer Emit } ? __VLS_types.EmitEvent2<Emit, '${prop.arg.loc.source}'> : unknown,\n`);
tsCodeGen.addText(`typeof ${varComponentInstance} extends { $emit: infer Emit } ? __VLS_types.EmitEvent2<Emit, '${camelizeName}'> : unknown,\n`);
tsCodeGen.addText(`],\n`);

if (key_3 !== key_2) {
tsCodeGen.addText(`${varInstanceProps}[`);
writeCodeWithQuotes(
key_3,
[{ start: prop.arg.loc.start.offset, end: prop.arg.loc.end.offset }],
{
vueTag: 'template',
capabilities: capabilitiesSet.attrReference,
normalizeNewName(newName) {
return 'on' + newName[0].toUpperCase() + newName.substring(1);
},
applyNewName(oldName, newName) {
const hName = hyphenate(newName);
if (hyphenate(newName).startsWith('on-')) {
return camelize(hName.slice('on-'.length));
}
return newName;
},
},
);
tsCodeGen.addText(`],\n`);
}
tsCodeGen.addText(`>\n`);
}
tsCodeGen.addText(`typeof ${varComponentInstance} extends { $emit: infer Emit } ? __VLS_types.EmitEvent2<Emit, '${prop.arg.loc.source}'> : unknown,\n`);
}
tsCodeGen.addText(`>\n`);
}
Expand Down
22 changes: 14 additions & 8 deletions packages/vue-language-service/testCases/typeChecks/events.vue
@@ -1,13 +1,13 @@
<template>
<div @click="exactType($event, {} as MouseEvent)"></div>

<C1 @foo-bar="exactType($event, {} as number)" />
<C1 @foo-bar="exactType($event, {} as number)" @bar-baz="exactType($event, {} as number)" />
<C2 @foo-bar="exactType($event, {} as number)" />
<C3 @foo-bar="exactType($event, {} as number)" />
<C4 value="1" @foo-bar="exactType($event, {} as string)" />
<C4 :value="1" @foo-bar="exactType($event, {} as number)" />
<C3 @foo-bar="exactType($event, {} as any)" />
<C4 value="1" @foo-bar="exactType($event, {} as any)" />
<C4 :value="1" @foo-bar="exactType($event, {} as any)" />

<C1 @fooBar="exactType($event, {} as number)" />
<C1 @fooBar="exactType($event, {} as number)" @barBaz="exactType($event, {} as any)" />
<C2 @fooBar="exactType($event, {} as number)" />
<C3 @fooBar="exactType($event, {} as number)" />
<C4 value="1" @fooBar="exactType($event, {} as string)" />
Expand All @@ -22,6 +22,9 @@
<C7 @click="exactType($event, {} as number)" />
<C8 @click="exactType($event, {} as number)" />

<!-- https://github.com/johnsoncodehk/volar/issues/1023 -->
<C10 @foo-bar="exactType($event, {} as number)"></C10>

<!-- invalid component type don't fallback to native event type -->
<C9 @click="exactType($event, {} as any)" />
</template>
Expand All @@ -31,18 +34,21 @@ import { defineComponent, FunctionalComponent, PropType } from 'vue';
import { exactType } from './shared';
import C5 from './events_union.vue';
const C1 = defineComponent({ emits: { fooBar: (_num: number) => true } });
const C1 = defineComponent({ emits: { fooBar: (_num: number) => true, 'bar-baz': (_num: number) => true } });
const C2 = defineComponent({ props: { onFooBar: {} as PropType<(num: number) => void> } });
const C6 = defineComponent({ props: { onFoo: {} as PropType<(_num: string) => void> }, emits: { foo: (_num: number) => true } });
const C7 = defineComponent({ emits: { click: (_num: number) => true } });
const C8 = defineComponent({ props: { onClick: {} as PropType<(_num: number) => void> } });
</script>

<script lang="ts">
declare const C3: FunctionalComponent<{}, { fooBar(num: number): true }>;
declare const C4: new <T>(props: { value: T }) => {
declare const C3: FunctionalComponent<{}, { fooBar(num: number): true; }>;
declare const C4: new <T>(props: { value: T; }) => {
$props: typeof props;
$emit: { (event: 'fooBar', e: T): void; };
};
declare const C9: new () => {};
declare const C10: new () => {
$props: { 'onFoo-bar'?: (num: number) => void; };
};
</script>
7 changes: 5 additions & 2 deletions packages/vue-typescript/src/utils/localTypes.ts
Expand Up @@ -119,9 +119,12 @@ export type EmitEvent_3<E2, E> =
: E2 extends AnyArray<infer K> ? (E extends K ? (...args: any) => void : unknown) // emits: ['event-1', 'event-2']
: E extends keyof E2 ? ReturnVoid<E2[E]> // emits: { 'event-1': () => true, 'event-2': () => true }
: unknown
export type FirstFunction<F0, F1> =
export type FirstFunction<F0 = void, F1 = void, F2 = void, F3 = void> =
NonNullable<F0> extends (Function | AnyArray<Function>) ? F0 :
NonNullable<F1> extends (Function | AnyArray<Function>) ? F1 : unknown;
NonNullable<F1> extends (Function | AnyArray<Function>) ? F1 :
NonNullable<F2> extends (Function | AnyArray<Function>) ? F2 :
NonNullable<F3> extends (Function | AnyArray<Function>) ? F3 :
unknown;
export type GlobalAttrsBase = VNodeProps & AllowedComponentProps;
export type GlobalAttrs = GlobalAttrsBase & HTMLAttributes;
export type PickComponents<T> = ComponentKeys<T> extends keyof T ? Pick<T, ComponentKeys<T>> : T;
Expand Down

0 comments on commit 6dc54dd

Please sign in to comment.