Skip to content

types: fix mount infer prop type #274

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions src/mount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ export function mount<V, P>(
props(Props: P): any
registerHooks(keys: string[]): void
},
options?: MountingOptions<P>
options?: MountingOptions<P & PublicProps>
): VueWrapper<ComponentPublicInstance<V>>

// Functional component with emits
export function mount<Props, E extends EmitsOptions = {}>(
originalComponent: FunctionalComponent<Props, E>,
options?: MountingOptions<Props>
options?: MountingOptions<Props & PublicProps>
): VueWrapper<ComponentPublicInstance<Props>>

// Component declared with defineComponent
Expand Down Expand Up @@ -104,7 +104,10 @@ export function mount<
Props,
Defaults
>,
options?: MountingOptions<Props, D>
options?: MountingOptions<
Partial<Defaults> & Omit<Props & PublicProps, keyof Defaults>,
D
>
): VueWrapper<
InstanceType<
DefineComponent<
Expand Down Expand Up @@ -147,7 +150,7 @@ export function mount<
Extends,
EE
>,
options?: MountingOptions<never, D>
options?: MountingOptions<Props & PublicProps, D>
): VueWrapper<
ComponentPublicInstance<Props, RawBindings, D, C, M, E, VNodeProps & Props>
>
Expand Down Expand Up @@ -179,7 +182,7 @@ export function mount<
EE,
Props
>,
options?: MountingOptions<Props, D>
options?: MountingOptions<Props & PublicProps, D>
): VueWrapper<ComponentPublicInstance<Props, RawBindings, D, C, M, E>>

// Component declared with { props: { ... } }
Expand Down Expand Up @@ -207,7 +210,7 @@ export function mount<
Extends,
EE
>,
options?: MountingOptions<ExtractPropTypes<PropsOptions>, D>
options?: MountingOptions<ExtractPropTypes<PropsOptions> & PublicProps, D>
): VueWrapper<
ComponentPublicInstance<
ExtractPropTypes<PropsOptions>,
Expand Down
14 changes: 12 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
Directive,
Plugin,
AppConfig,
VNode
VNode,
VNodeProps
} from 'vue'

interface RefSelector {
Expand Down Expand Up @@ -32,6 +33,15 @@ type SlotDictionary = {
[key: string]: Slot
}

// From vue next
// https://github.com/vuejs/vue-next/blob/1f2a652a9d2e3bec472fb1786a4c16d6ccfa1fb1/packages/runtime-core/src/h.ts#L53-L58
type RawProps = VNodeProps & {
// used to differ from a single VNode object as children
__v_isVNode?: never
// used to differ from Array children
[Symbol.iterator]?: never
} & Record<string, any>

export interface MountingOptions<Props, Data = {}> {
/**
* Overrides component's default data. Must be a function.
Expand All @@ -42,7 +52,7 @@ export interface MountingOptions<Props, Data = {}> {
* Sets component props when mounted.
* @see https://vue-test-utils.vuejs.org/v2/api/#props
*/
props?: Props
props?: (RawProps & Props) | ({} extends Props ? null : never)
/**
* @deprecated use `data` instead.
*/
Expand Down
93 changes: 59 additions & 34 deletions test-dts/mount.d-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,10 @@ expectType<string>(
// })
// )

// can not receive extra props
expectError(
mount(AppWithDefine, {
props: { a: 'Hello', c: 2 }
})
)
// allow extra props, like using `h()`
mount(AppWithDefine, {
props: { a: 'Hello', c: 2 }
})

// wrong prop type should not compile
expectError(
Expand All @@ -75,12 +73,9 @@ expectType<string>(
}).vm.a
)

// can't receive extra props
expectError(
mount(AppWithProps, {
props: { a: 'Hello', b: 2 }
})
)
mount(AppWithProps, {
props: { a: 'Hello', b: 2 }
})

// wrong prop type should not compile
expectError(
Expand Down Expand Up @@ -109,35 +104,25 @@ expectType<number>(
}).vm.b
)

// cannot receive extra props
// if they pass use object inside
expectError(
mount(
{
props: ['a']
},
{
props: {
b: 2
}
// allow extra props, like using `h()`
mount(
{
props: ['a']
},
{
props: {
b: 2
}
)
}
)

const AppWithoutProps = {
template: ''
}

// can't receive extra props
expectError(
mount(AppWithoutProps, {
props: { b: 'Hello' }
})
)

// except if explicitly cast
// allow extra props, like using `h()`
mount(AppWithoutProps, {
props: { b: 'Hello' } as never
props: { b: 'Hello' }
})

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

expectType<number>(
mount((props: { a: 1 }, ctx) => {}, {
mount((props: { a: number }, ctx) => {}, {
props: {
a: 22
}
Expand Down Expand Up @@ -226,3 +211,43 @@ class ClassComponent extends Vue {
// @ts-expect-error it requires an argument
expectError(mount(ClassComponent, {}).vm.changeMessage())
mount(ClassComponent, {}).vm.changeMessage('')

// default props
const Foo = defineComponent({
props: {
bar: Boolean,
baz: String
},
template: ''
})

mount(Foo, {
props: {
baz: 'hello'
}
})

mount(Foo, {
props: {
bar: true
}
})

expectError(
mount(
defineComponent({
props: {
baz: String,
bar: {
type: Boolean,
required: true
}
}
}),
{
props: {
baz: 'hello'
}
}
)
)
33 changes: 11 additions & 22 deletions test-dts/shallowMount.d-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,10 @@ let wrapper = shallowMount(AppWithDefine, {
// vm is properly typed
expectType<string>(wrapper.vm.a)

// can not receive extra props
expectError(
shallowMount(AppWithDefine, {
props: { a: 'Hello', c: 2 }
})
)
// allow extra props, like using `h()`
shallowMount(AppWithDefine, {
props: { a: 'Hello', c: 2 }
})

// wrong prop type should not compile
expectError(
Expand All @@ -53,12 +51,10 @@ expectType<string>(
}).vm.a
)

// can't receive extra props
expectError(
shallowMount(AppWithProps, {
props: { a: 'Hello', b: 2 }
})
)
// allow extra props, like using `h()`
shallowMount(AppWithProps, {
props: { a: 'Hello', b: 2 }
})

// wrong prop type should not compile
expectError(
Expand Down Expand Up @@ -89,16 +85,9 @@ const AppWithoutProps = {
template: ''
}

// can't receive extra props
expectError(
(wrapper = shallowMount(AppWithoutProps, {
props: { b: 'Hello' }
}))
)

// except if explicitly cast
shallowMount(AppWithoutProps, {
props: { b: 'Hello' } as never
// allow extra props, like using `h()`
wrapper = shallowMount(AppWithoutProps, {
props: { b: 'Hello' }
})

// class component
Expand Down