Skip to content

Commit 7b3029e

Browse files
committed
fix: allow extra props of components in TSX
1 parent a5e6ad5 commit 7b3029e

File tree

3 files changed

+98
-17
lines changed

3 files changed

+98
-17
lines changed

src/helpers.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import {
33
UnwrapRef,
44
ComponentObjectPropsOptions,
55
ExtractPropTypes,
6+
AllowedComponentProps,
7+
ComponentCustomProps,
8+
VNodeProps,
69
} from 'vue'
710

811
import {
@@ -64,23 +67,22 @@ export type ExtractInstance<T> = T extends VueMixin<infer V> ? V : never
6467
export type NarrowEmit<T extends VueBase> = Omit<
6568
T,
6669
'$emit' | keyof ClassComponentHooks
67-
>
70+
> &
6871
// Reassign class component hooks as mapped types makes prototype function (`mounted(): void`) instance function (`mounted: () => void`).
69-
& ClassComponentHooks
70-
71-
// Exclude generic $emit type (`$emit: (event: string, ...args: any[]) => void`) if there are another intersected type.
72-
& {
73-
$emit: T['$emit'] extends (((event: string, ...args: any[]) => void) & infer R)
74-
? unknown extends R
75-
? T['$emit']
76-
: R
77-
: T['$emit']
72+
ClassComponentHooks & {
73+
// Exclude generic $emit type (`$emit: (event: string, ...args: any[]) => void`) if there are another intersected type.
74+
$emit: T['$emit'] extends ((event: string, ...args: any[]) => void) &
75+
infer R
76+
? unknown extends R
77+
? T['$emit']
78+
: R
79+
: T['$emit']
7880
}
7981

8082
export type MixedVueBase<Mixins extends VueMixin[]> = Mixins extends (infer T)[]
8183
? VueConstructor<
82-
NarrowEmit<UnionToIntersection<ExtractInstance<T>> & Vue> & VueBase
83-
>
84+
NarrowEmit<UnionToIntersection<ExtractInstance<T>> & Vue> & VueBase
85+
>
8486
: never
8587

8688
export function mixins<T extends VueMixin[]>(...Ctors: T): MixedVueBase<T>
@@ -96,7 +98,7 @@ export function mixins(...Ctors: VueMixin[]): VueConstructor {
9698
Ctors.forEach((Ctor) => {
9799
const data = new (Ctor as VueConstructor)(...args)
98100
Object.keys(data).forEach((key) => {
99-
; (this as any)[key] = (data as any)[key]
101+
;(this as any)[key] = (data as any)[key]
100102
})
101103
})
102104
}
@@ -106,12 +108,22 @@ export function mixins(...Ctors: VueMixin[]): VueConstructor {
106108
export function props<
107109
PropNames extends string,
108110
Props = Readonly<{ [key in PropNames]?: any }>
109-
>(propNames: PropNames[]): VueConstructor<Vue<Props> & Props>
111+
>(
112+
propNames: PropNames[]
113+
): VueConstructor<
114+
Vue<Props, {}, VNodeProps & AllowedComponentProps & ComponentCustomProps> &
115+
Props
116+
>
110117

111118
export function props<
112119
PropsOptions extends ComponentObjectPropsOptions,
113120
Props = Readonly<ExtractPropTypes<PropsOptions>>
114-
>(propsOptions: PropsOptions): VueConstructor<Vue<Props> & Props>
121+
>(
122+
propsOptions: PropsOptions
123+
): VueConstructor<
124+
Vue<Props, {}, VNodeProps & AllowedComponentProps & ComponentCustomProps> &
125+
Props
126+
>
115127

116128
export function props(
117129
propsOptions: string[] | ComponentObjectPropsOptions

src/vue.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import {
44
ComponentOptions,
55
VNode,
66
SetupContext,
7+
VNodeProps,
8+
AllowedComponentProps,
9+
ComponentCustomProps,
710
} from 'vue'
811

912
function defineGetter<T, K extends keyof T>(
@@ -115,8 +118,12 @@ export type EmitsOptions = ObjectEmitsOptions | string[]
115118

116119
export type Vue<
117120
Props = unknown,
118-
Emits extends EmitsOptions = {}
119-
> = ComponentPublicInstance<{}, {}, {}, {}, {}, Emits, Props> &
121+
Emits extends EmitsOptions = {},
122+
PublicProps = Props &
123+
VNodeProps &
124+
AllowedComponentProps &
125+
ComponentCustomProps
126+
> = ComponentPublicInstance<Props, {}, {}, {}, {}, Emits, PublicProps> &
120127
ClassComponentHooks
121128

122129
export interface VueConstructor<V extends VueBase = Vue> extends VueMixin<V> {

test-dts/jsx.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { Vue, props } from '../src'
2+
3+
describe('JSX', () => {
4+
function assertJsx(jsx: JSX.Element): void {}
5+
6+
it('checks props type', () => {
7+
const Props = props({
8+
required: {
9+
type: String,
10+
required: true,
11+
},
12+
13+
optional: Number,
14+
})
15+
16+
class App extends Props {}
17+
18+
assertJsx(<App required="Hello" />)
19+
assertJsx(<App required="Hello" optional={123} />)
20+
21+
// @ts-expect-error
22+
assertJsx(<App />)
23+
// @ts-expect-error
24+
assertJsx(<App required={true} />)
25+
// @ts-expect-error
26+
assertJsx(<App required="Hello" optional={{ foo: 'bar' }} />)
27+
28+
assertJsx(
29+
<App ref="app" class="foo" required="Test" testCustomProp="custom" />
30+
)
31+
})
32+
33+
it('passes vnode props', () => {
34+
class App extends Vue {}
35+
36+
assertJsx(<App ref="test" />)
37+
assertJsx(<App key="123" />)
38+
})
39+
40+
it('passes class and style attributes', () => {
41+
class App extends Vue {}
42+
43+
assertJsx(<App class="foo" />)
44+
assertJsx(<App class={{ disabled: true }} />)
45+
assertJsx(<App class={['test']} />)
46+
47+
assertJsx(<App style="font-size: 12px;" />)
48+
assertJsx(<App style={{ color: 'red' }} />)
49+
})
50+
51+
it('passes component custom props', () => {
52+
class App extends Vue {}
53+
54+
assertJsx(<App testCustomProp="test" />)
55+
})
56+
})
57+
58+
declare module '@vue/runtime-core' {
59+
interface ComponentCustomProps {
60+
testCustomProp?: string
61+
}
62+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy