[TypeScript] Allow HTML attributes to Vue component with type, if an attribute has the same name with the component's prop, keep the prop type instead of intersection types
Vue version
3.3.4
Link to minimal reproduction
Steps to reproduce
The Vue component cannot accept HTML attributes with type in TypeScript, or get any
type.
It won't get suggestion of HTML attributes when typing in a component props. When typing a native HTML event in component, its parameters will get any
type, so TypeScript will report an error.
The API shows that we can extends the props for all components with types.
Just create a .d.ts
file in the project.
types/vue.d.ts
declare module "vue" {
export interface AllowedComponentProps extends HTMLAttributes {}
}
Then all components will accept HTML attributes in TypeScript.
If I create a component:
components/MyComponent.vue
<script setup lang="ts">
defineProps<{
id: number;
}>();
defineEmits<{
click: [num: number];
}>();
</script>
And use it with:
<MyComponent
:id="1"
lang="en"
@click="num => handleNumber(num)"
@mouseenter="e => handleMouseEvent(e)"
/>
What is expected?
The id
prop is defined in MyComponent, so it will use the prop type number
instead of HTML attribute id
type string
;
The lang
prop isn't defined in MyComponent, so it will use HTML attribute lang
type string
instead of unknown
or any
.
The click
event is defined in MyComponent, so it will use the emit type (num: number) => void
instead of HTML attribute onClick
type (payload: MouseEvent) => void
;
The mouseenter
event isn't defined in MyComponent, so it will use HTML attribute onMouseenter
type (payload: MouseEvent) => void
instead of unknown
or any
.
What is actually happening?
Without interface AllowedComponentProps extends HTMLAttributes
declaration
Prop or emit | Actually type | Expected type | Notes |
---|---|---|---|
id |
number |
number |
|
lang |
any |
string |
|
click |
(num: number) => void |
(num: number) => void |
|
mouseenter |
any |
(payload: MouseEvent) => void |
Get TypeScript error: Parameter e implicitly has an any type |
With interface AllowedComponentProps extends HTMLAttributes
declaration
Prop or emit | Actually type | Expected type | Notes |
---|---|---|---|
id |
never |
number |
Trying to merge number & string so that get never |
lang |
string |
string |
|
click |
(((num: number) => any) & ((payload: MouseEvent) => void)) |
(e: number) => void |
The parameter num or payload will get never type |
mouseenter |
(payload: MouseEvent) => void |
(payload: MouseEvent) => void |
System Info
System:
OS: Windows 10 10.0.22621
CPU: (8) x64 Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz
Memory: 1.30 GB / 11.80 GB
Binaries:
Node: 19.0.1 - D:\Program Files\nodejs\node.EXE
Yarn: 1.22.17 - ~\AppData\Roaming\npm\yarn.CMD
npm: 8.19.2 - D:\Program Files\nodejs\npm.CMD
pnpm: 7.26.3 - ~\AppData\Roaming\npm\pnpm.CMD
Browsers:
Edge: Spartan (44.22621.1702.0), Chromium (115.0.1901.203)
Internet Explorer: 11.0.22621.1
Any additional comments?
No response
ButtonHTMLAttributes
I am just using the button as an example and should actually support all elements.
I found the solution.
In file @vue/runtime-core/dist/runtime-core.d.ts
, find the type declaration of ComponentPublicInstance
, then change its property $props
from
$props: Prettify<MakeDefaultsOptional extends true ? Partial<Defaults> & Omit<P & PublicProps, keyof Defaults> : P & PublicProps>;
to
$props: Prettify<MakeDefaultsOptional extends true ? Partial<Defaults> & Omit<Override<PublicProps, P>, keyof Defaults> : Override<PublicProps, P>>;
Where type Override
is
type Override<T, U> = Omit<T, keyof U> & U;
I found the solution.
In file
@vue/runtime-core/dist/runtime-core.d.ts
, find the type declaration ofComponentPublicInstance
, then change its property$props
from$props: Prettify<MakeDefaultsOptional extends true ? Partial<Defaults> & Omit<P & PublicProps, keyof Defaults> : P & PublicProps>;
to
$props: Prettify<MakeDefaultsOptional extends true ? Partial<Defaults> & Omit<Override<PublicProps, P>, keyof Defaults> : Override<PublicProps, P>>;
Where type
Override
istype Override<T, U> = Omit<T, keyof U> & U;
@otomad wow, LGTM. Looking forward to your PR for this issue.