Skip to content

Commit c5d3130

Browse files
authored
Merge pull request vuejs#274 from pikax/fix/set_default_props_as_optional
types: fix mount infer prop type
2 parents fbe5f2b + 8a1c678 commit c5d3130

File tree

4 files changed

+91
-64
lines changed

4 files changed

+91
-64
lines changed

src/mount.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,13 @@ export function mount<V, P>(
6767
props(Props: P): any
6868
registerHooks(keys: string[]): void
6969
},
70-
options?: MountingOptions<P>
70+
options?: MountingOptions<P & PublicProps>
7171
): VueWrapper<ComponentPublicInstance<V>>
7272

7373
// Functional component with emits
7474
export function mount<Props, E extends EmitsOptions = {}>(
7575
originalComponent: FunctionalComponent<Props, E>,
76-
options?: MountingOptions<Props>
76+
options?: MountingOptions<Props & PublicProps>
7777
): VueWrapper<ComponentPublicInstance<Props>>
7878

7979
// Component declared with defineComponent
@@ -105,7 +105,10 @@ export function mount<
105105
Props,
106106
Defaults
107107
>,
108-
options?: MountingOptions<Props, D>
108+
options?: MountingOptions<
109+
Partial<Defaults> & Omit<Props & PublicProps, keyof Defaults>,
110+
D
111+
>
109112
): VueWrapper<
110113
InstanceType<
111114
DefineComponent<
@@ -148,7 +151,7 @@ export function mount<
148151
Extends,
149152
EE
150153
>,
151-
options?: MountingOptions<never, D>
154+
options?: MountingOptions<Props & PublicProps, D>
152155
): VueWrapper<
153156
ComponentPublicInstance<Props, RawBindings, D, C, M, E, VNodeProps & Props>
154157
>
@@ -180,7 +183,7 @@ export function mount<
180183
EE,
181184
Props
182185
>,
183-
options?: MountingOptions<Props, D>
186+
options?: MountingOptions<Props & PublicProps, D>
184187
): VueWrapper<ComponentPublicInstance<Props, RawBindings, D, C, M, E>>
185188

186189
// Component declared with { props: { ... } }
@@ -208,7 +211,7 @@ export function mount<
208211
Extends,
209212
EE
210213
>,
211-
options?: MountingOptions<ExtractPropTypes<PropsOptions>, D>
214+
options?: MountingOptions<ExtractPropTypes<PropsOptions> & PublicProps, D>
212215
): VueWrapper<
213216
ComponentPublicInstance<
214217
ExtractPropTypes<PropsOptions>,

src/types.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
Directive,
55
Plugin,
66
AppConfig,
7-
VNode
7+
VNode,
8+
VNodeProps
89
} from 'vue'
910

1011
interface RefSelector {
@@ -32,6 +33,15 @@ type SlotDictionary = {
3233
[key: string]: Slot
3334
}
3435

36+
// From vue next
37+
// https://github.com/vuejs/vue-next/blob/1f2a652a9d2e3bec472fb1786a4c16d6ccfa1fb1/packages/runtime-core/src/h.ts#L53-L58
38+
type RawProps = VNodeProps & {
39+
// used to differ from a single VNode object as children
40+
__v_isVNode?: never
41+
// used to differ from Array children
42+
[Symbol.iterator]?: never
43+
} & Record<string, any>
44+
3545
export interface MountingOptions<Props, Data = {}> {
3646
/**
3747
* Overrides component's default data. Must be a function.
@@ -42,7 +52,7 @@ export interface MountingOptions<Props, Data = {}> {
4252
* Sets component props when mounted.
4353
* @see https://vue-test-utils.vuejs.org/v2/api/#props
4454
*/
45-
props?: Props
55+
props?: (RawProps & Props) | ({} extends Props ? null : never)
4656
/**
4757
* @deprecated use `data` instead.
4858
*/

test-dts/mount.d-test.ts

Lines changed: 59 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,10 @@ expectType<string>(
4444
// })
4545
// )
4646

47-
// can not receive extra props
48-
expectError(
49-
mount(AppWithDefine, {
50-
props: { a: 'Hello', c: 2 }
51-
})
52-
)
47+
// allow extra props, like using `h()`
48+
mount(AppWithDefine, {
49+
props: { a: 'Hello', c: 2 }
50+
})
5351

5452
// wrong prop type should not compile
5553
expectError(
@@ -75,12 +73,9 @@ expectType<string>(
7573
}).vm.a
7674
)
7775

78-
// can't receive extra props
79-
expectError(
80-
mount(AppWithProps, {
81-
props: { a: 'Hello', b: 2 }
82-
})
83-
)
76+
mount(AppWithProps, {
77+
props: { a: 'Hello', b: 2 }
78+
})
8479

8580
// wrong prop type should not compile
8681
expectError(
@@ -109,35 +104,25 @@ expectType<number>(
109104
}).vm.b
110105
)
111106

112-
// cannot receive extra props
113-
// if they pass use object inside
114-
expectError(
115-
mount(
116-
{
117-
props: ['a']
118-
},
119-
{
120-
props: {
121-
b: 2
122-
}
107+
// allow extra props, like using `h()`
108+
mount(
109+
{
110+
props: ['a']
111+
},
112+
{
113+
props: {
114+
b: 2
123115
}
124-
)
116+
}
125117
)
126118

127119
const AppWithoutProps = {
128120
template: ''
129121
}
130122

131-
// can't receive extra props
132-
expectError(
133-
mount(AppWithoutProps, {
134-
props: { b: 'Hello' }
135-
})
136-
)
137-
138-
// except if explicitly cast
123+
// allow extra props, like using `h()`
139124
mount(AppWithoutProps, {
140-
props: { b: 'Hello' } as never
125+
props: { b: 'Hello' }
141126
})
142127

143128
// Functional tests
@@ -150,7 +135,7 @@ expectError((props: { a: 1 }) => {}, {
150135
})
151136

152137
expectType<number>(
153-
mount((props: { a: 1 }, ctx) => {}, {
138+
mount((props: { a: number }, ctx) => {}, {
154139
props: {
155140
a: 22
156141
}
@@ -226,3 +211,43 @@ class ClassComponent extends Vue {
226211
// @ts-expect-error it requires an argument
227212
expectError(mount(ClassComponent, {}).vm.changeMessage())
228213
mount(ClassComponent, {}).vm.changeMessage('')
214+
215+
// default props
216+
const Foo = defineComponent({
217+
props: {
218+
bar: Boolean,
219+
baz: String
220+
},
221+
template: ''
222+
})
223+
224+
mount(Foo, {
225+
props: {
226+
baz: 'hello'
227+
}
228+
})
229+
230+
mount(Foo, {
231+
props: {
232+
bar: true
233+
}
234+
})
235+
236+
expectError(
237+
mount(
238+
defineComponent({
239+
props: {
240+
baz: String,
241+
bar: {
242+
type: Boolean,
243+
required: true
244+
}
245+
}
246+
}),
247+
{
248+
props: {
249+
baz: 'hello'
250+
}
251+
}
252+
)
253+
)

test-dts/shallowMount.d-test.ts

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,10 @@ let wrapper = shallowMount(AppWithDefine, {
2121
// vm is properly typed
2222
expectType<string>(wrapper.vm.a)
2323

24-
// can not receive extra props
25-
expectError(
26-
shallowMount(AppWithDefine, {
27-
props: { a: 'Hello', c: 2 }
28-
})
29-
)
24+
// allow extra props, like using `h()`
25+
shallowMount(AppWithDefine, {
26+
props: { a: 'Hello', c: 2 }
27+
})
3028

3129
// wrong prop type should not compile
3230
expectError(
@@ -53,12 +51,10 @@ expectType<string>(
5351
}).vm.a
5452
)
5553

56-
// can't receive extra props
57-
expectError(
58-
shallowMount(AppWithProps, {
59-
props: { a: 'Hello', b: 2 }
60-
})
61-
)
54+
// allow extra props, like using `h()`
55+
shallowMount(AppWithProps, {
56+
props: { a: 'Hello', b: 2 }
57+
})
6258

6359
// wrong prop type should not compile
6460
expectError(
@@ -89,16 +85,9 @@ const AppWithoutProps = {
8985
template: ''
9086
}
9187

92-
// can't receive extra props
93-
expectError(
94-
(wrapper = shallowMount(AppWithoutProps, {
95-
props: { b: 'Hello' }
96-
}))
97-
)
98-
99-
// except if explicitly cast
100-
shallowMount(AppWithoutProps, {
101-
props: { b: 'Hello' } as never
88+
// allow extra props, like using `h()`
89+
wrapper = shallowMount(AppWithoutProps, {
90+
props: { b: 'Hello' }
10291
})
10392

10493
// class component

0 commit comments

Comments
 (0)