Skip to content

Commit 81dec4a

Browse files
authored
fix(language-core): intersect __VLS_slots with __VLS_ctx.$slots (#5083)
1 parent 7da8b85 commit 81dec4a

File tree

6 files changed

+68
-54
lines changed

6 files changed

+68
-54
lines changed

packages/language-core/lib/codegen/globalTypes.ts

+3
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ export function generateGlobalTypes({
126126
'__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: infer Ctx } ? Ctx : never : any
127127
, T extends (props: any, ctx: infer Ctx) => any ? Ctx : any
128128
>>;
129+
type __VLS_OmitStringIndex<T> = {
130+
[K in keyof T as string extends K ? never : K]: T[K];
131+
};
129132
type __VLS_UseTemplateRef<T> = Readonly<import('${lib}').ShallowRef<T | null>>;
130133
131134
function __VLS_getVForSourceType(source: number): [number, number][];

packages/language-core/lib/codegen/template/context.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,14 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
107107
const accessExternalVariables = new Map<string, Set<number>>();
108108
const slots: {
109109
name: string;
110-
loc?: number;
110+
offset?: number;
111111
tagRange: [number, number];
112-
varName: string;
113112
nodeLoc: any;
113+
propsVar: string;
114114
}[] = [];
115115
const dynamicSlots: {
116116
expVar: string;
117-
varName: string;
117+
propsVar: string;
118118
}[] = [];
119119
const blockConditions: string[] = [];
120120
const scopedClasses: {

packages/language-core/lib/codegen/template/index.ts

+30-25
Original file line numberDiff line numberDiff line change
@@ -67,34 +67,39 @@ function* generateSlots(
6767
options: TemplateCodegenOptions,
6868
ctx: TemplateCodegenContext
6969
): Generator<Code> {
70+
const name = getSlotsPropertyName(options.vueCompilerOptions.target);
7071
if (!options.hasDefineSlots) {
71-
yield `var __VLS_slots!: __VLS_PrettifyGlobal<{}`;
72-
for (const { expVar, varName } of ctx.dynamicSlots) {
73-
ctx.hasSlot = true;
74-
yield `${newLine}& { [K in NonNullable<typeof ${expVar}>]?: (props: typeof ${varName}) => any }`;
75-
}
76-
for (const slot of ctx.slots) {
77-
yield `${newLine}& { `;
78-
ctx.hasSlot = true;
79-
if (slot.name && slot.loc !== undefined) {
80-
yield* generateObjectProperty(
81-
options,
82-
ctx,
83-
slot.name,
84-
slot.loc,
85-
ctx.codeFeatures.withoutHighlightAndCompletion,
86-
slot.nodeLoc
87-
);
72+
yield `var __VLS_slots!: __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.${name}>`;
73+
if (ctx.dynamicSlots.length || ctx.slots.length) {
74+
yield ` & Readonly<`;
75+
for (const { expVar, propsVar } of ctx.dynamicSlots) {
76+
ctx.hasSlot = true;
77+
yield `${newLine}& { [K in NonNullable<typeof ${expVar}>]?: (props: typeof ${propsVar}) => any }`;
8878
}
89-
else {
90-
yield* wrapWith(
91-
slot.tagRange[0],
92-
slot.tagRange[1],
93-
ctx.codeFeatures.withoutHighlightAndCompletion,
94-
`default`
95-
);
79+
for (const slot of ctx.slots) {
80+
yield `${newLine}& { `;
81+
ctx.hasSlot = true;
82+
if (slot.name && slot.offset !== undefined) {
83+
yield* generateObjectProperty(
84+
options,
85+
ctx,
86+
slot.name,
87+
slot.offset,
88+
ctx.codeFeatures.withoutHighlightAndCompletion,
89+
slot.nodeLoc
90+
);
91+
}
92+
else {
93+
yield* wrapWith(
94+
slot.tagRange[0],
95+
slot.tagRange[1],
96+
ctx.codeFeatures.withoutHighlightAndCompletion,
97+
`default`
98+
);
99+
}
100+
yield `?: (props: typeof ${slot.propsVar}) => any }`;
96101
}
97-
yield `?: (props: typeof ${slot.varName}) => any }`;
102+
yield `${newLine}>`;
98103
}
99104
yield `>${endOfLine}`;
100105
}

packages/language-core/lib/codegen/template/slotOutlet.ts

+12-14
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function* generateSlotOutlet(
1414
node: CompilerDOM.SlotOutletNode
1515
): Generator<Code> {
1616
const startTagOffset = node.loc.start.offset + options.template.content.slice(node.loc.start.offset).indexOf(node.tag);
17-
const varSlot = ctx.getInternalVariable();
17+
const propsVar = ctx.getInternalVariable();
1818
const nameProp = node.props.find(prop => {
1919
if (prop.type === CompilerDOM.NodeTypes.ATTRIBUTE) {
2020
return prop.name === 'name';
@@ -43,7 +43,7 @@ export function* generateSlotOutlet(
4343
? `'${nameProp.value.content}'`
4444
: nameProp?.type === CompilerDOM.NodeTypes.DIRECTIVE && nameProp.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
4545
? nameProp.exp.content
46-
: `('default' as const)`
46+
: `'default'`
4747
),
4848
`]`
4949
);
@@ -66,7 +66,7 @@ export function* generateSlotOutlet(
6666
yield `)${endOfLine}`;
6767
}
6868
else {
69-
yield `var ${varSlot} = {${newLine}`;
69+
yield `var ${propsVar} = {${newLine}`;
7070
yield* generateElementProps(
7171
options,
7272
ctx,
@@ -83,10 +83,10 @@ export function* generateSlotOutlet(
8383
) {
8484
ctx.slots.push({
8585
name: nameProp.value.content,
86-
loc: nameProp.loc.start.offset + nameProp.loc.source.indexOf(nameProp.value.content, nameProp.name.length),
86+
offset: nameProp.loc.start.offset + nameProp.loc.source.indexOf(nameProp.value.content, nameProp.name.length),
8787
tagRange: [startTagOffset, startTagOffset + node.tag.length],
88-
varName: varSlot,
8988
nodeLoc: node.loc,
89+
propsVar,
9090
});
9191
}
9292
else if (
@@ -97,31 +97,29 @@ export function* generateSlotOutlet(
9797
if (isShortHand) {
9898
ctx.inlayHints.push(createVBindShorthandInlayHintInfo(nameProp.exp.loc, 'name'));
9999
}
100-
const slotExpVar = ctx.getInternalVariable();
101-
yield `var ${slotExpVar} = `;
100+
const expVar = ctx.getInternalVariable();
101+
yield `var ${expVar} = __VLS_tryAsConstant(`;
102102
yield* generateInterpolation(
103103
options,
104104
ctx,
105105
'template',
106106
ctx.codeFeatures.all,
107107
nameProp.exp.content,
108108
nameProp.exp.loc.start.offset,
109-
nameProp.exp,
110-
'(',
111-
')'
109+
nameProp.exp
112110
);
113-
yield ` as const${endOfLine}`;
111+
yield `)${endOfLine}`;
114112
ctx.dynamicSlots.push({
115-
expVar: slotExpVar,
116-
varName: varSlot,
113+
expVar,
114+
propsVar,
117115
});
118116
}
119117
else {
120118
ctx.slots.push({
121119
name: 'default',
122120
tagRange: [startTagOffset, startTagOffset + node.tag.length],
123-
varName: varSlot,
124121
nodeLoc: node.loc,
122+
propsVar,
125123
});
126124
}
127125
}

packages/tsc/tests/__snapshots__/dts.spec.ts.snap

+11-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ declare const _default: <Row extends BaseRow>(__VLS_props: NonNullable<Awaited<t
1212
expose(exposed: import("vue").ShallowUnwrapRef<{}>): void;
1313
attrs: any;
1414
slots: {
15-
default?: (props: {
15+
readonly default?: (props: {
1616
row: Row;
1717
}) => any;
1818
};
@@ -594,7 +594,8 @@ export {};
594594
`;
595595
596596
exports[`vue-tsc-dts > Input: template-slots/component.vue, Output: template-slots/component.vue.d.ts 1`] = `
597-
"declare var __VLS_0: {};
597+
"declare const __VLS_ctx: InstanceType<__VLS_PickNotAny<typeof __VLS_self, new () => {}>>;
598+
declare var __VLS_0: {};
598599
declare var __VLS_1: {
599600
num: number;
600601
};
@@ -605,16 +606,17 @@ declare var __VLS_3: {
605606
num: number;
606607
str: string;
607608
};
608-
declare var __VLS_slots: __VLS_PrettifyGlobal<{} & {
609+
declare var __VLS_slots: __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$slots> & Readonly<{
609610
'no-bind'?: (props: typeof __VLS_0) => any;
610611
} & {
611612
default?: (props: typeof __VLS_1) => any;
612613
} & {
613614
'named-slot'?: (props: typeof __VLS_2) => any;
614615
} & {
615616
vbind?: (props: typeof __VLS_3) => any;
616-
}>;
617+
}>>;
617618
type __VLS_TemplateSlots = typeof __VLS_slots;
619+
declare const __VLS_self: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
618620
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
619621
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateSlots>;
620622
export default _default;
@@ -688,7 +690,8 @@ type __VLS_WithTemplateSlots<T, S> = T & {
688690
`;
689691
690692
exports[`vue-tsc-dts > Input: template-slots/component-no-script.vue, Output: template-slots/component-no-script.vue.d.ts 1`] = `
691-
"declare var __VLS_0: {};
693+
"declare const __VLS_ctx: InstanceType<__VLS_PickNotAny<typeof __VLS_self, new () => {}>>;
694+
declare var __VLS_0: {};
692695
declare var __VLS_1: {
693696
num: number;
694697
};
@@ -699,16 +702,17 @@ declare var __VLS_3: {
699702
num: number;
700703
str: string;
701704
};
702-
declare var __VLS_slots: __VLS_PrettifyGlobal<{} & {
705+
declare var __VLS_slots: __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$slots> & Readonly<{
703706
'no-bind'?: (props: typeof __VLS_0) => any;
704707
} & {
705708
default?: (props: typeof __VLS_1) => any;
706709
} & {
707710
'named-slot'?: (props: typeof __VLS_2) => any;
708711
} & {
709712
vbind?: (props: typeof __VLS_3) => any;
710-
}>;
713+
}>>;
711714
type __VLS_TemplateSlots = typeof __VLS_slots;
715+
declare const __VLS_self: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
712716
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
713717
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateSlots>;
714718
export default _default;

test-workspace/tsc/passedFixtures/vue3/slots/main.vue

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<!-- $slots type -->
2+
<!-- component slots type -->
33
<Comp value="1">
44
<template #foo="bindings">{{ exactType(bindings, {} as string) }}</template>
55
</Comp>
@@ -26,7 +26,10 @@
2626
</template>
2727

2828
<script lang="ts">
29-
export default { name: 'Self' };
29+
export default {
30+
name: 'Self',
31+
slots: Object as SlotsType<{ foo?: (_: any) => any }>,
32+
};
3033
3134
declare const Comp: new <T>(props: { value: T; }) => {
3235
$props: typeof props;
@@ -37,14 +40,15 @@ declare const Comp: new <T>(props: { value: T; }) => {
3740
</script>
3841

3942
<script lang="ts" setup>
40-
import { ref, useSlots, VNode } from 'vue';
43+
import { ref, type SlotsType, useSlots, type VNode } from 'vue';
4144
import { exactType } from '../../shared';
4245
4346
const baz = ref('baz' as const);
4447
4548
const slots = useSlots();
46-
exactType(slots, {} as {
49+
exactType(slots, {} as Readonly<{
50+
foo?: (props: any) => any;
4751
bar?: (props: { str: string; num: number; }) => any;
4852
baz?: (props: { str: string; num: number; }) => any;
49-
});
53+
}>);
5054
</script>

0 commit comments

Comments
 (0)