context.attrs This is defined in RFC 31. 2. In that case, setup()'s first argument, props, is an empty object. This is not explicitly defined in the RFC above, but can be kind of expected.defineComponent allows us to pass specific types for props through the first generic argument, but we can't set specific types for context.attrs - it's always a plain Data type unless I manually typecast it in setup, which I find to be less than ideal.Example:
type TabsProps = {
modelValue: string
}
export const Tabs = defineComponent<TabsProps>({
name: 'Tabs',
//props: is not set,
,inheritAttrs: false,
setup(props, { slots, attrs }) {
console.log(JSON.stringify(props, null, 2)) // => {}
console.log(JSON.stringify(attrs, null, 2)) // => { "modelValue": "some string" }
// in the following lines, wrapProp() is a generic helper function that uses `keyof` to wrap a specific prop in a typesafe way.
// 1. Using `props`: TS is satisfied, as to TS, there's a `modelValue prop`
// but it fails but fails at runtime as `props` is really an empty object
const state = wrapProp(props, 'modelValue')
// 2. Using `attrs`: this works at runtime,
// but for TS, `state` is now ComputedRef<unknown>, as the type of the prop could not be inferred from `Data`
const state = wrapProp(attrs, 'modelValue')
// do something with `state` here, i.e. pass it to a composable....
return () => h(props.tag ?? 'DIV', slots.default?.())
},
})
We have two possible solutions here:
props: options are specified, the props argument contains all of the attributes, which means it contains the same content as attrs.defineComponent in a way that we can type attrs like we can now type props.I personally prefer the first way - if no props: options are defined, all attrs are also possibly props at the same time, so to me it makes sense that both contain the same references.
Are attrs automatically available in the template like props are?
they are through $attrs like today in Vue 2.
Hi! Any feedback on this issue? I have some components that are basically just registries with some logic and I would love to be able to just use props in setup instead of duplicating the props option everywhere or using attrs, which becomes confusing in this case
How would type checking work in the templates if it's using attrs instead
of props
On Tue, Jun 9, 2020, 8:28 PM PrettyWood notifications@github.com wrote:
Hi! Any feedback on this issue? I have some components that are basically
just registries with some logic and I would love to be able to just use
props in setup instead of duplicating the props option everywhere—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/vuejs/vue-next/issues/1155#issuecomment-641462562,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAAKROQKCKLMUOXRVWGJVJDRVZWLDANCNFSM4M5GK5ZA
.
This is actually close to what Optional Props Declaration is, and has the same problem when dealing with attrs fallthrough:
No fallthrough -> use class and style on the component but find them not applied to root, confusing
Fallthrough -> all the props you intend to use for state only are also applied to root as attributes. Also confusing.
Essentially, if we expose attrs as props when no props options are declared, it leads to ambiguous behavior for attr fallthrough.
I notice you are using inheritAttrs: false because you are aware of the problem above, but making props behave the same as attrs in this case makes it too easy for users to use it without being aware of the issue and later get surprised when they find out about it.
Another issue here is that in the current implementation, attrs is not reactive, because they are supposed to be only used in the render function and not as reactive state. If you want to do reactive work with something, you should declare it as props.
In this case, I also don't really see a reason to not use the props option, since that would give the prop type inference. The only reason I guess is preferring to declare props using TS types - which, while tempting, unfortunately doesn't provide enough runtime information to Vue to avoid ambiguity.
To solve that, we may need to introduce a separate option, e.g. optionalProps: true, which:
inhertiAttrs: false (or, allows class, style and v-on fallthrough just like functional components)thisprops in setup function, as $props in templates, and as this.$props in options API. Also reactive.not in runtime, compile to "props" by loader instead, is it better?
export interface PropsType {
title: string;
}
let ShorterWay = defineComponent<PropsType>((props) => {
return () => (
<p>{props.title}</p>
)
})
// compile to
export interface PropsType {
title: string;
}
let longWay = defineComponent({
props: {
title:String,
},
setup: (props) => {
return () => (
<p>{props.title}</p>
)
}
})
defineComponent({
props: {
title:{
type: String,
required: true,
},
},
setup: (props) => {
return () => (
<p>{props.title}</p>
)
}
})
code like above is tedious.
I think this is better:
defineComponent<{title:string, subTitle?: string}>({
props: ['title', 'subTitle'], // this is to tell the difference between props and attrs
setup: (props) => {
return () => (
<p>{props.title} <span>{props.subTitle}</span></p>
)
}
})
Most helpful comment
code like above is tedious.
I think this is better: