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

defineSlots for script setup #4092

Closed
jods4 opened this issue Jul 9, 2021 · 9 comments
Closed

defineSlots for script setup #4092

jods4 opened this issue Jul 9, 2021 · 9 comments

Comments

@jods4
Copy link
Contributor

jods4 commented Jul 9, 2021

What problem does this feature solve?

(I'm sure I saw this mentioned somewhere before, but I can't find any issue nor discussion about it. Apologies if it's a dupe.)

For Typescript users, script setup needs a defineSlots macro to create strongly typed slots.

The problem is that today the code generated by script setup creates TS compilation errors.

The following template creates an error Binding element 'ok' implicitly has an 'any' type.
(You have the same error even if you don't destructure the slot value.)

<my-dialog v-slot="{ ok, cancel }">
  <button @click="ok()">Close</button>
</my-dialog>

This is quite a blocker because there's no good workaround:

  • You can't add type annotations inside templates (and in this instance it would be very ugly very quickly);
  • You can't add ts-ignore comments in the template.
  • This errors is only reported with --noImplicitAny but that's a strict flag that we really don't want to globally disable in our codebase.

Of course, on top of the TS error, having typed slots would be a nice improvement for Volar.

What does the proposed API look like?

As a quick-fix / workaround, I suggest that the compiler adds // @ts-ignore: implicit any before the generated slot code.

A better, long term fix would be to allow TS users to type their slots, I suggest:

defineSlots<{
  default: {
    quantity: number;
    order(): void;
  }
}>();
@jods4 jods4 changed the title defineSlot for script setup defineSlots for script setup Jul 9, 2021
@jods4
Copy link
Contributor Author

jods4 commented Jul 9, 2021

A small post-scriptum:
I have build errors when the consuming template is a script setup component.

How the component itself is declared seems irrelevant, so that probably means a slots: { ... } addition to classical <script> would keep everything consistent.

@jods4
Copy link
Contributor Author

jods4 commented Jul 13, 2021

I don't know since when, but today I noticed that Volar is able to type slots that are inside components defined by script setup, when they're used in the same project.

That's super cool and a great QoL improvement ✨

It would be super-duper awesome if there was a way to couple that Volar inferrence with a way to make props/slots generic.
I put one real-life example of what it could achieve in this discussion:
vuejs/rfcs#334 (comment)

@yyx990803
Copy link
Member

I'm curious what is the real benefit of type checking generated code here - vue-tsc can type check *.vue files directly so you'd preferably use that - which works on your source code providing more accurate error locations. Then you'd use esbuild or transpile-only mode of TS for faster builds. Am I missing something?

@jods4
Copy link
Contributor Author

jods4 commented Jul 14, 2021

Yes, I agree.

I opened this when my slot was implicitly typed as any in IDE.
Now I think this was because the component providing the slot was a script-classic, as it seems that script-setup is inferred by Volar / vue-tsc.

There's no need for manually declaring defineSlots if they can be automatically inferred.
Maybe this issue should have been that a slot: { default: ... } would be welcome in script-classic? Or was my slot implicitly any for a different reason at all? I could have been confused, this is all still experimental to me.

A few extra thoughts / considerations

(1) I'm building with TS not Esbuild, mainly because of const enums. Speed is always good but for production build is not the top priority. Doing 2 TS passes (one with vue-tsc then regular build with TS) is a bit overkill, I opened this discussion about some possible solutions: johnsoncodehk/vue-tsc#48

(2) I am curious how this will all turn out with generic components, which would be a complex, albeit much needed, addition.
For example, if you have a component that provides a slot value only one of its props is filled or has a specific type.

Consider a kind of Form component, that injects a validation object iff its rules property is set:

<template>
  <form>
    <slot :val="val" />
  </form>
</template>

<script setup>
  // Not sure what the syntax in script setup would be...
  defineGeneric<M extends object, R extends Rules<M> | undefined>();

  defineProps<{ model: M, rules?: R }>();

  // This is injected in slot, its type should be Validation if rules props is not undefined, otherwise it's undefined.
  const val: R extends Rules<M> ? Validation<M, R> : undefined;
</script>

It can all be modelled by TS type system, but when you use the component in a template, I have no idea if it would even be possible to infer the right types!

<!--
Here we need to infer:
M: { name: string }
R: { name: required }
  
So TS can then:
- Validate R satisfies Rules<M>
- Type val as `Validation<M, R>` (not undefined)
--> 
<my-form :model="{ name: 'first' }" :rules="{ name: required }" v-slot="{ val }" />

<!--
This time we should infer:
R: undefined
Then val is `undefined` and trying to use it will result in a compilation error.
--> 
<my-form :model="{ name: 'first' }" v-slot="{ val }" />

@yyx990803
Copy link
Member

If the remaining utility is only related to generic components, I think it would be better discussed as part of the generic components proposal. So closing this one here.

@ccqgithub
Copy link

ccqgithub commented Apr 10, 2022

If the remaining utility is only related to generic components, I think it would be better discussed as part of the generic components proposal. So closing this one here.

@yyx990803 如果使用volar开发,现在项目中能有scopedSlots props的类型提示,但是貌似是结合模版解析提示的。

但是如果构建一个第三方组件库,.vue文件会编译成 js 文件,这时候会丢失slots的类型信息(因为现在的defineComponent里面没有slots的类型,所以生成的.d.ts文件貌似不能保留slots 的类型?),引用这类组件库就得不到scopedSlots props的类型提示。

所以在现有的defineComponents里加入slots类型变量,以及script setup里加入defineSlots好像有必要?

或者可以在现有的useSlots上加上泛型?

const slots = useSlots<Slots>();

@paya-cz
Copy link

paya-cz commented Apr 27, 2022

This should be reopened. I use the latest Volar yet I do not have any type interference for slot props. I get type any just like the OP did.

@MueckeMa
Copy link

I also find this feature useful when I have slots in child components and would like to define them in the Enclosing Component. I don't know any solution for this.

@sxzz
Copy link
Member

sxzz commented Apr 3, 2023

Done in #7982

@github-actions github-actions bot locked and limited conversation to collaborators Sep 12, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants