Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typescript error when trying to expose slot to parents using generic #11671

Closed
AloisH opened this issue Aug 20, 2024 · 7 comments · May be fixed by #11725
Closed

Typescript error when trying to expose slot to parents using generic #11671

AloisH opened this issue Aug 20, 2024 · 7 comments · May be fixed by #11725

Comments

@AloisH
Copy link

AloisH commented Aug 20, 2024

Vue version

3.4.38

Link to minimal reproduction

https://github.com/AloisH/vue-3-generic-type-bug-with-slots

Steps to reproduce

Clone the repository https://github.com/AloisH/vue-3-generic-type-bug-with-slots
Run npm install
Run npm run type-check

How to manualy reproduce the error:

Here is a simple example on how to reproduce it

ParentComp.vue

<template>
    <div>
        Hello!
    </div>
    <ChildComp :value="value">
        <template v-for="(_, name) in $slots" :key="name" v-slot:[name]="slotProps">
            <slot :name="name" v-bind="slotProps || {}">
            </slot>
        </template>
    </ChildComp>
</template>

<script setup lang="ts" generic="T">
import ChildComp from './ChildComp.vue';

defineProps<{
    value: T;
}>();
</script>

ChildComp

<template>
    <div>
        <slot name="hello" :value="value"></slot>
    </div>
</template>

<script setup lang="ts" generic="T">
defineSlots<{
    hello?(): any;
}>();

defineProps<{ value: T }>(
);
</script>

This only happens to generic template
if we remove from ChildComp the generic no more typescript error will occur

Example with working version:

ChildComp

<template>
    <div>
        <slot name="hello" :value="value"></slot>
    </div>
</template>

<script setup lang="ts">
defineSlots<{
    hello?(): any;
}>();

defineProps<{ value: any }>(
);
</script>

What is expected?

No error from typescript

What is actually happening?

We found 2 errors

src/components/MainComp.vue:6:67 - error TS2537: Type 'Readonly<{ hello?(): any; }> & { hello?(): any; }' has no matching index signature for type 'number'.

6         <template v-for="(_, name) in $slots" :key="name" v-slot:[name]="slotProps">
                                                                    ~~~~

src/components/MainComp.vue:6:67 - error TS2537: Type 'Readonly<{ hello?(): any; }> & { hello?(): any; }' has no matching index signature for type 'string'.

6         <template v-for="(_, name) in $slots" :key="name" v-slot:[name]="slotProps">
                                                                    ~~~~


Found 2 errors.

System Info

System:
    OS: Linux 5.15 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish)
    CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
    Memory: 13.01 GB / 15.58 GB
    Container: Yes
    Shell: 3.3.1 - /usr/bin/fish
  Binaries:
    Node: 18.20.3 - ~/.volta/tools/image/node/18.20.3/bin/node
    npm: 10.7.0 - ~/.volta/tools/image/node/18.20.3/bin/npm
    pnpm: 9.0.6 - ~/.local/share/pnpm/pnpm
    bun: 1.1.8 - ~/.bun/bin/bun
  npmPackages:
    vue: ^3.4.29 => 3.4.38

Any additional comments?

No response

@edison1105
Copy link
Member

/cc @so1ve

@Procrustes5
Copy link
Contributor

Hello, I've been thinking about this.

How about making Slot type a bit more flexible?

export type Slot<T extends any = any> = (
...args: IfAny<T, any[], [T] | (T extends undefined ? [] : never)>
) => VNode[]

I think changing type of normalizeSlot to Generic also effective.

const normalizeSlot = (
key: string,
rawSlot: Function,
ctx: ComponentInternalInstance | null | undefined,
): Slot => {
if ((rawSlot as any)._n) {
// already normalized - #5353
return rawSlot as Slot
}
const normalized = withCtx((...args: any[]) => {
if (
__DEV__ &&
currentInstance &&
(!ctx || ctx.root === currentInstance.root)
) {
warn(
`Slot "${key}" invoked outside of the render function: ` +
`this will not track dependencies used in the slot. ` +
`Invoke the slot function inside the render function instead.`,
)
}
return normalizeSlotValue(rawSlot(...args))
}, ctx) as Slot
// NOT a compiled slot
;(normalized as ContextualRenderFn)._c = false
return normalized
}

const normalizeSlot = <T extends any>(
  key: string,
  rawSlot: Slot<T>,
  ctx: ComponentInternalInstance | null | undefined,
): Slot<T> => {

}

What do you think? Let me know if you spot any potential issues or have ideas to make it even better!

@edison1105
Copy link
Member

@Procrustes5 PR welcome.

@edison1105
Copy link
Member

edison1105 commented Aug 30, 2024

@AloisH

  • Vue - Official v2.1.2
  • Upgrade vue-tsc to the latest version ^2.1.2

Works as expected

@AloisH
Copy link
Author

AloisH commented Aug 30, 2024

Thanks!

@AloisH
Copy link
Author

AloisH commented Aug 30, 2024

Should I close the issue ?

@edison1105
Copy link
Member

Closing as not related to core

@edison1105 edison1105 closed this as not planned Won't fix, can't repro, duplicate, stale Aug 30, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Sep 14, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants