Open pikax opened 4 years ago
Wouldn't it be better to add it to attributes.json
for Vetur so it doesn't bloat the final bundle?
I think it would be better to have automated tools to generate attributes.json
and web-types.json
, also this would allow to get the types even on a render function using h
, something the *.json
can't do it.
EDIT: this would also allow us to check at runtime if the user is trying to use a slot that doesn't exist.
How would you type slots that depend on props?
How would you type slots that depend on props?
You would need to type it the same as props (duplicating it).
If this is primarily a TypeScript type hinting wouldn't it be better to export slot typing separately?
<script lang="ts">
export type slots = { default: { message: string } }
...
</script>
πfor typed slots but I am not a fan of having an extra option that will bloat my production build unless it will be possible to remove these declarations by babel plugin?
If this is primarily a TypeScript type hinting wouldn't it be better to export slot typing separately?
Needs to work with defineComponent
aka outside of SFC
.
πfor typed slots but I am not a fan of having an extra option that will bloat my production build unless it will be possible to remove these declarations by babel plugin?
I'm thinking doing something similar to typed emit
, but when writing this RFC thought it would be cool to give an warning like we do with props
.
Needs to work with defineComponent aka outside of SFC.
But how these typings are going to be useful outside of SFC? I thought that the main idea was to provide type hints inside of <template/>
block in SFC (since they're used a lot more regularly than render functions).
Also, what prevents template compiler from doing just that? It knows for sure which slots are available from a component, the only thing missing is prop types, but I guess it is doable as well?
<slot :foo="foo as string" />
Related issue: https://github.com/vuejs/vue-next/issues/1359
But how these typings are going to be useful outside of SFC?
Better typing will be useful everywhere where we need to use the slots, this will be on the component who describes the slots
and the component who injects the slot
.
export default defineComponent({
slots: {
top: null, // no value
item: Object as () => { item: { value: number }; i: number },
},
setup(_, { slots }) {
const items = Array.from({ length: 10 }).map((_, value) => ({ value }))
return () => {
return h('div', [
//slots.top({a: 1}), // type error no args expected
//slots.random({a: 1}), // type error no slot defined
slots.top(),
items.map((item, i) => h('div', slots.item && slots.item({ item, i }))),
])
}
},
})
I thought that the main idea was to provide type hints inside of
<template/>
block in SFC (since they're used a lot more regularly than render functions).
The motivation is better Typescript support! That's not only on the template but anywhere we will use component.
Also, what prevents template compiler from doing just that? It knows for sure which slots are available from a component, the only thing missing is prop types, but I guess it is doable as well?
Render function support.
<slot :foo="foo as string" />
Related issue: vuejs/vue-next#1359
I don't believe this has anything to do with that, this RFC is providing types only for slots, that is to provide typescript support on the template.
I don't suppose there's a way to solve:
export default defineComponent({
setup(props, { slots }) {
return () => {
return h('div', [
props.items.map(item => slots[item.name] && slots[item.name]({ item })),
// or
props.items.map(item => slots[`${item.name}-cell`] && slots[`${item.name}-cell`]({ item })),
])
}
},
})
where slots are dynamic?
You can't describe that with typescript :/
I was support slot type check by IDE with no additional options: https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar
But for third party library like vue-router
, I can't read the typescript source code to calculate v-slot types, for this case I need to define additional options for my tool like this: https://github.com/johnsoncodehk/volar#v-slot-type-checking
So if have a slots option let typescript can emit the slots types to .d.ts
, this is helpful for third party library.
Vue 3 already has emits
option with list of emitted events. One of two main roles (as I see it) is helping IDE and other development tools.
Vue has props
to define props. Now Vue has emits
to define events. It sounds very logical and attractive to have slots
to define components' slots. Still component interface is: props + events + provide/inject + slots, it would make component interface fully defined.
And about dynamic slot names. This issue is the same as Dynamic event names in emits option.
So, it seems slots are a part of component interface similar to emits with similar problems, and it would be great to have all defined.
It makes sense to make components more type-safe by statically typing their slots interface.
Just want to point out that as is it creates more TS confusion.
Why did you type the slot with a function Object as () => slotType
instead of re-using the same pattern as props: as PropType<T>
? That adds more arbitrary ways to do similar stuff that needs to be learnt by devs.
More generally that's not great DX and should fall in the same umbrella as the discussion you started in #282 for props.
So I π the concept/idea but the fine API details could need some TS love.
Just want to point out that as is it creates more TS confusion. Why did you type the slot with a function
Object as () => slotType
instead of re-using the same pattern as props:as PropType<T>
? That adds more arbitrary ways to do similar stuff that needs to be learnt by devs.
Naming wise they are different, PropType
is as the name states: describes a type of a prop
. Reusing it to describe a Slot
will cause more confusion, just because they "do similar stuff", does not mean they are the same. Not to mention that there're fields such as validator
and default
, that don't make sense on Slot
context, required
would be arguable, but I don't see much usage from a required slots
since slots are usually "defaulted"(<slot name="title">Component title</slot>
).
Object as () => slotType
That's optional API, probably could be used to make sure the parameter passed to the slot
could be runtime validated (but probably doesn't make much sense).
That's a optional API, similar to props
, but that's completely optional and you can describe it as an purely typescript type by (https://github.com/vuejs/vue-next/pull/2693):
defineComponent({
slots: {} as {
test: null
item: { item: { value: number }; i: number }
},
setup(_, { slots }) {
slots.test!()
slots.item!({
i: 22,
item: {
value: 22
}
})
}
})
Also other API that could be possible would be a similar to the emits
defineComponent({
slots: {
test(){
},
item(item: {value: number}) {
}
}
})
But the slot can only take one parameter, which will probably confuse some users, and the execution of the function would not be used (besides runtime validation - already mentioned above).
More generally that's not great DX and should fall in the same umbrella as the discussion you started in #282 for props.
That RFC is unrelated to this one.
So I π the concept/idea but the fine API details could need some TS love.
If you have more feedback, please let me know.
Naming wise they are different, PropType is as the name states: describes a type of a prop. Reusing it to describe a Slot will cause more confusion, just because they "do similar stuff", does not mean they are the same.
I agree, but if they're similar then they should have similar API.
For example call it SlotType<T>
, or create a more generically usable type alias type VueType<T> = PropType<T>
that has a non-specific name and that we could use everywhere.
Or do you plan on back-porting that new syntax to type props
like that as well? That would work, too.
Not to mention that there're fields such as validator and default, that don't make sense on Slot context, required would be arguable, but I don't see much usage from a required slots since slots are usually "defaulted"(
Component title ).
That would be PropOptions
.
PropType
itself is just about the type:
https://github.com/vuejs/vue-next/blob/4a965802e883107a2af00301a59fb7f403b6acf7/packages/runtime-core/src/componentProps.ts#L49-L56
That's optional API, probably could be used to make sure the parameter passed to the slot could be runtime validated (but probably doesn't make much sense).
It probably doesn't make that much sense because the component defines the slot and provides it -- as opposed to props where the component defines the props and users provides values.
The same could be said of the emit
typing, though, and it does provide a way to validate its arguments, so I dunno. π€·ββοΈ
That's a optional API, similar to props, but that's completely optional and you can describe it as an purely typescript type by (vuejs/vue-next#2693):
That's nice! I suppose slots: SomeInterface
would work just as well, you don't need the empty object, do you?
I wish real props and emits could work like that.
That RFC is unrelated to this one.
Not directly, but I feel like we should have a unified, easy way to do all the typings in TS, which is a bit what #282 is about (I think?) albeit focused on props.
So I π the concept/idea but the fine API details could need some TS love. If you have more feedback, please let me know.
Now that you've shown me that slots: T
is possible I'm taking back that comment and I think it's a nice way to type in TS.
A random thought I had while thinking about those things: do you think attributes.json
or web-types.json
are the right way to approach an exported contract that tooling/compiler could use to validate a component usage?
Shouldn't we piggy back TS own type definitions?
We could let TS export a .d.ts
for each component and that would contain the props, emits, slots... everything that's needed to know about a component interface.
It's just a random idea, I haven't thought this through.
For example call it
SlotType<T>
, or create a more generically usable type aliastype VueType<T> = PropType<T>
that has a non-specific name and that we could use everywhere.
Still need to think about it, VueType
might be a bit too generic for this, naming wise I'm still not settle or have strong opinions.
It probably doesn't make that much sense because the component defines the slot and provides it -- as opposed to props where the component defines the props and users provides values.
The same could be said of the
emit
typing, though, and it does provide a way to validate its arguments, so I dunno. π€·ββοΈ
I have the same opinion, I can see it be useful and be useless, I guess that can leave that to the developers, like we do on the emit
That's nice! I suppose
slots: SomeInterface
would work just as well, you don't need the empty object, do you?
It won't work because of Typescript,. you would need to cast something to your expected type/interface.
I wish real props and emits could work like that.
props
are validated at runtime, so an object or array is required, to allow a pure typescript declaration we need to make a few assumptions see Allow assign all attributes as propsemits
you should be able to do just typing, but you will get the warning atdev
.Not directly, but I feel like we should have a unified, easy way to do all the typings in TS, which is a bit what #282 is about (I think?) albeit focused on props.
I would like to keep the scope of each RFC as small as possible, props
and slots
behave differently (runtime checks and no check respectively). The relation is the Typescript, which the #282 is not only a Typescript approach.
A random thought I had while thinking about those things: do you think
attributes.json
orweb-types.json
are the right way to approach an exported contract that tooling/compiler could use to validate a component usage?
There's no concise way yet to describe a lot of things, each IDE/PLUGIN has it's own way to declare types, hopefully this RFC will allow us to describe more component related types on the component through typescript.
My goal is: You should be allowed to describe all the necessary things on the component, such as props
, slots
, emits
, etc.
We could let TS export a
.d.ts
for each component and that would contain the props, emits, slots... everything that's needed to know about a component interface.
Yep, you should be able to do that already, but that's the goal.
This probably isn't possible with the current API but generic slot types would also be useful, for example if I have a component that iterates over an array passed to its items
prop:
defineComponent(<T extends any>() => ({ // I think this is already used to pass a setup function directly
props: {
items: Array as PropType<T[]>
},
slots: null as {
item: SlotType<{ item: T }>
},
setup (props, { slots }) {
return () => (
<ul>{
props.items.map(item => (
<li>{ slots.item({ item }) }</li>
))
}</ul>
)
}
}))
Then using it could be completely safe:
<my-list :items="items">
<template #item="{ item }">
{{ item.bar }} <!-- Property 'bar' does not exist on type '{ foo: string; }' -->
</template>
</my-list>
<script>
export default {
data: () => ({
items: [{ foo: '' }]
})
}
</script>
@KaelWD that's a timely comment! Recently I've been trying Volar that typechecks your templates and I had a lot of issues similar to your example.
When I have a bit of time I'm gonna open a discussion in vue-rfcs to argue that we need generic components. Slots is one part of it, but even simple props need it, otherwise you get lots of errors in non-basic scenarios.
It's gonna be challenging but I believe it's the only way if we want type-safe templates.
Oh yeah definitely, we have stuff like this too:
defineComponent(<T extends object>() => ({
props: {
items: Array as PropType<T[]>,
itemKey: String as PropType<keyof T>
}
}))
@KaelWD I wrote my thoughts about why generics are needed to fully enable type-safe views in #334. I'm interested in your similar experience, if you can comment?
I want to defineComponent like this:
defineComponent<
<T, T1, T2>() =>
| {
$props: {
value: T
foo: T1
bar: T2
titleRenderProp: (value?: T) => VNode
}
$emit: {
(event: 'update:value', value: T): void
}
}
| {
$props: {
value: T
foo: [T1]
bar: [T1, T2]
}
$emit: {
(event: 'update:value', value: T): void
}
$slots: {
title: (slotProps: { value?: T }) => VNode[] | undefined
}
}
>({
props: ['value'],
setup() {}
})
Because the types of emits
, slots
, and props
often affect each other.
It's very helpful to me!
Will this ever be solved? Makes TS almost useless in large Vue3 projects, especially when one is writing a lot of reusable/library code. Generic components and typed slots are must IMO.
I just add the defineSlots
feature for Vue Macros (unplugin-vue-macros@0.14.0-beta.2
). Even though we have smart Volar helping us to detect slots in the template and generate type info, it's not enough I think.
So I implement this feature. With it, it's possible to declare slot type in <script setup>
.
<script setup lang="ts">
defineSlots<{
// slot name
title: {
// scoped slot
foo: 'bar' | boolean
}
}>()
</script>
It would be so great to have this finalized!
Allow to describe
slots
andtypes
SFC
Render
Type only
Rendered