Allow constraining ref attribute to not allow string when using tsx.
What problem does this feature solve?
When writing a component in tsx, passing a string to a ref
attribute on a native element does not work and typescript does not tell you that anything is wrong.
This is not a problem with custom components because we can override ref
typing like:
import * as RuntimeCore from '@vue/runtime-core'
declare module '@vue/runtime-core' {
declare interface ComponentCustomProps {
ref?:
| RuntimeCore.Ref
| ((ref: Element | RuntimeCore.ComponentInternalInstance | null) => void)
}
}
What does the proposed API look like?
I'm not sure the best way to solve this problem but here is where the problem is:
// @vue/runtime-dom/dist
type ReservedProps = {
key?: string | number
ref?:
| string
| RuntimeCore.Ref
| ((ref: Element | RuntimeCore.ComponentInternalInstance | null) => void)
}
// need to be able to override to say
type ReservedProps = {
key?: string | number
ref?:
| RuntimeCore.Ref
| ((ref: Element | RuntimeCore.ComponentInternalInstance | null) => void)
}
This should work:
defineComponent({
setup() {
const dom = ref(null)
return {
dom
}
},
render() {
return <div ref="dom"></div>
}
})
what do you actually mean?
Using jsx-next, recommended here, for writing tsx files and using this syntax.
import { defineComponent } from "vue";
defineComponent({
setup() {
const dom = ref(null)
return () => (
<div ref={dom} />
);
},
});
jsx-next provides two syntaxes for writing render functions. It does work the way you wrote it but it does not work the way I wrote it. I prefer returning a render function from setup as it's more succinct and typescript tells me when a custom component is not within the template scope. jsx-next offers a way to write it the way I like so I'd just like to be able to control what is allowed for ref values on native input elements. For those that choose this method for their whole application they should be able to restrict what is passed to ref to suit their style.
// Bulkier syntax
import { defineComponent } from "vue";
import CustomComponent from './CustomComponent'
defineComponent({
components: {
CustomComponent
}
setup() {
const dom = ref(null)
return {
dom,
title: 'info'
}
},
render () {
return (
<div ref="dom" >
<CustomComponent title={this.title} />
</div>
);
}
});
// more succinct syntax
import { defineComponent } from "vue";
import CustomComponent from './CustomComponent'
defineComponent({
setup() {
const dom = ref(null)
const title = 'info'
return () => (
<div ref={dom}>
<CustomComponent title={title} />
</div>
);
},
});
To be clear this doesn't work and I'd like typescript to tell me what what is being passed to ref is an error
import { defineComponent } from "vue";
import CustomComponent from './CustomComponent'
defineComponent({
setup() {
const dom = ref(null)
const title = 'info'
return () => (
<div ref="dom">
<CustomComponent title={title} />
</div>
);
},
});
I know what you mean: you mean that if jsx is used as the return value of setup then it is not allowed to be specified as a string, otherwise, it can. But we cannot restrict ref to not be able to specify a string, because it actually allows the use of string types. And I don’t think we can limit ref correctly unless we can analyze its context correctly.
I'm not saying that vue should limit it for everyone. I'm saying that consumers of vue should be able to customize it for their own purposes. For example, by adding this to my application type definition files I fix this issue for custom components, because it ends up overriding what is defined in @vue/runtime-dom
.
import * as RuntimeCore from '@vue/runtime-core'
declare module '@vue/runtime-core' {
declare interface ComponentCustomProps {
ref?:
| RuntimeCore.Ref
| ((ref: Element | RuntimeCore.ComponentInternalInstance | null) => void)
}
}
now typescript will hint that ref is incorrect below
import { defineComponent } from "vue";
import CustomComponent from './CustomComponent'
defineComponent({
setup() {
const customRef = ref(null)
return () => (
<CustomComponent ref="customRef" />
);
},
});
I'd like the same ability to define a type definition file in my application to override what is in @vue/runtime-dom
so that for my application you cannot pass strings to a native element ref attribute.
@trainiac I made a PR https://github.com/vuejs/vue-next/pull/3458, but I’m not sure if it will eventually be accepted