rfcs
rfcs copied to clipboard
Different external and internal names for props
Allow aliasing props, to have different external and internal names (some languages call these "argument labels"):
props: {
externalName: {
as: 'internalName'
}
}
Also at https://github.com/vuejs/vue/issues/7943
I like the idea, nice work!
One thing I'm concerned about (but not qualified to answer) is how this complicates typings for Typescript.
Nice. I think alias get's my vote, reads better without further explanation:
props: {
ageOfPerson: {
type: Number,
alias: 'age'
}
}
Hmm, if we call it alias it'd have to work like an actual alias in my opinion, meaning both names would work when passing props – the same way aliases in Vue Router don't replace the path of a route but, well, alias it.
So for a component Greet.vue with
{
props: {
fullName: {
type: String,
alias: 'name',
}
}
these would have to result in identical output:
<Greet name="Eddy Example" />
<Greet full-name="Eddy Example" />
If we don't want an alias but "to have different external and internal names" then as is more accurate.
TL;DR: If it's an actual alias and both names are valid when passing props, call it alias. Otherwise as (or maybe something completely else) is more accurate.
this is great! I like the initial idea "as", since it is intuitive coming from js module imports
import { externalName as name } from 'someModule'
Agree with @jonaskuske that if it's "alias", then it looks like the prop can be used by either name, which can be confusing since props are usually a form of documentation.
Might it be able to accept multiple aliases by providing an array?
{
props: {
fullName: {
type: String,
alias: ['name', 'fullname'],
}
}
Might it be able to accept multiple aliases by providing an array
I would say that's overcomplicating it, if you want to provide an extra alias, you can do that in your own data method.
For real aliases you can already utilise computed properties
props: ['external'],
computed: {
internal () {
return this.external
}
}
Sure this is a little bit verbose but it elegantly arises from Vues natural capabilities.
EDIT 1: ok sorry, I didn't read the full RFC.
EDIT 2: Hmm... I still think I have a point though. You just really really want to keep using the external prop name even when you plug a computed property in-between. I think that's a rather trivial use case though.
There is an API available for provide/inject feature to have different local name, I think we should use similar API for props.
props: {
localName: {
from: ‘externalName’
}
}
https://vuejs.org/v2/api/#provide-inject
@JosephSilber I really like this. A lot. I think you could expand the RFC text to mention that when/if it can combined with computed instead of data, it also essentially enables the ability to do prop coercion and set prop defaults based on other props:
Stateful prop defaults
{
props: {
link: String,
linkType: {
type: String,
as: 'rawLinkType'
}
}
computed: {
linkType(){ return this.rawLinkType || this.link.startsWith('mailto:') ? 'email' : 'default' }
}
}
Prop coercion
{
props: {
title: {
type: String,
as: 'rawTitle'
}
}
computed: {
title(){ return titleCase(this.title) }
}
}
I think the examples list in the Motivation section of the RFC (as well as some use cases commented) are not good arguments for this proposal.
- This example expresses its intent quite accurately, the prop name indicates that it's the initial value, which is different from the internal mutable value. I think making this distinction is preferable.
props: ['initialCounter'],
data() {
return {
counter: this.initialCounter
}
}
- This example is a bit representative for a lot of arguments around the previously commented use cases of props coercion, sanitization or preprocessing in general.
props: ['size'],
computed: {
normalizedSize() {
return this.size.trim().toLowerCase()
}
}
With the introduction of Composition API, those use cases could be handled elegantly:
props: ['size'],
setup(props) {
const size = computed(() => {
return props.size.trim().toLowerCase()
})
return {
size
}
}
That being said, I'm not entirely against this. The Swift example in the RFC regarding argument labels is what in the similar vein with this proposal. Take that example into the context of Vue:
props: {
person: String,
from: {
as: 'hometown',
type: String
}
}
And take the example of the new <teleport> component (though it's not implemented with normal component options). Its target prop name is to, but it's not ideal to refer it internally as to.
For the sole purpose of renaming (or aliasing), we could do it in setup function:
props: {
to: {
type: [String, Element]
}
},
setup(props) {
const target = toRef(props, 'to')
// setting up
return {
target
}
}
But it's not as expressive and cohesive as following IMO:
props: {
to: {
as: 'target',
type: [String, Element]
}
},
setup(props) {
// setting up
return {
//
}
}
So, in summary:
- IMHO it's a bit of a detour to use this feature to solve the problems of props preprocessing of sort.
- This proposal can be useful for providing the feature of argument labels.
Just to clarify: it's not possible to solve that with Composition, you'll get an error for using a property with the same name as in props.
I think this change complicates things a lot more than it actually helps a developer. TypeScript support would be poor for this and probably require manually typing your props. It would also be unclear what value is being used in context without looking at props declaration first. Additionally since you can't mutate props but probably are going to use that value as an initial value in data that alone introduces confusion: component takes a prop foo then internally mutates foo context value. It's much more clear when we have foo as a prop and currentFoo as an internal state. Lastly, it can be mitigated by wrapping internal state into an object, which I think has a well balanced developer experience.
https://github.com/vuejs/rfcs/discussions/513 It might resolve this issue.