import { type Component, type DefineComponent, Fragment, type FunctionalComponent, Suspense, Teleport, type VNode, defineComponent, h, ref, resolveComponent, } from 'vue' import { describe, expectAssignable, expectType } from './utils' describe('h inference w/ element', () => { // key h('div', { key: 1 }) h('div', { key: 'foo' }) // @ts-expect-error h('div', { key: [] }) // @ts-expect-error h('div', { key: {} }) // ref h('div', { ref: 'foo' }) h('div', { ref: ref(null) }) h('div', { ref: _el => {} }) // @ts-expect-error h('div', { ref: [] }) // @ts-expect-error h('div', { ref: {} }) // @ts-expect-error h('div', { ref: 123 }) // slots const slots = { default: () => {} } // RawSlots h('div', {}, slots) // events h('div', { onClick: e => { expectType(e) }, }) h('input', { onFocus(e) { expectType(e) }, }) }) describe('h inference w/ Fragment', () => { // only accepts array children h(Fragment, ['hello']) h(Fragment, { key: 123 }, ['hello']) // @ts-expect-error h(Fragment, 'foo') // @ts-expect-error h(Fragment, { key: 123 }, 'bar') }) describe('h inference w/ Teleport', () => { h(Teleport, { to: '#foo' }, 'hello') h(Teleport, { to: '#foo' }, { default() {} }) // @ts-expect-error h(Teleport) // @ts-expect-error h(Teleport, {}) // @ts-expect-error h(Teleport, { to: '#foo' }) }) describe('h inference w/ Suspense', () => { h(Suspense, { onRecede: () => {}, onResolve: () => {} }, 'hello') h(Suspense, 'foo') h(Suspense, () => 'foo') h(Suspense, null, { default: () => 'foo', }) // @ts-expect-error h(Suspense, { onResolve: 1 }) }) declare const fc: FunctionalComponent< { foo: string bar?: number onClick: (evt: MouseEvent) => void }, ['click'], { default: () => VNode title: (scope: { id: number }) => VNode } > declare const vnode: VNode describe('h inference w/ functional component', () => { const Func = (_props: { foo: string; bar?: number }) => '' h(Func, { foo: 'hello' }) h(Func, { foo: 'hello', bar: 123 }) // @ts-expect-error h(Func, { foo: 123 }) // @ts-expect-error h(Func, {}) // @ts-expect-error h(Func, { bar: 123 }) h( fc, { foo: 'hello', onClick: () => {} }, { default: () => vnode, title: ({ id }: { id: number }) => vnode, }, ) }) describe('h support w/ plain object component', () => { const Foo = { props: { foo: String, }, } h(Foo, { foo: 'ok' }) h(Foo, { foo: 'ok', class: 'extra' }) // no inference in this case }) describe('h inference w/ defineComponent', () => { const Foo = defineComponent({ props: { foo: String, bar: { type: Number, required: true, }, }, }) h(Foo, { bar: 1 }) h(Foo, { bar: 1, foo: 'ok' }) // should allow extraneous props (attrs fallthrough) h(Foo, { bar: 1, foo: 'ok', class: 'extra' }) // @ts-expect-error should fail on missing required prop h(Foo, {}) // @ts-expect-error h(Foo, { foo: 'ok' }) // @ts-expect-error should fail on wrong type h(Foo, { bar: 1, foo: 1 }) }) // describe('h inference w/ defineComponent + optional props', () => { // const Foo = defineComponent({ // setup(_props: { foo?: string; bar: number }) {} // }) // h(Foo, { bar: 1 }) // h(Foo, { bar: 1, foo: 'ok' }) // // should allow extraneous props (attrs fallthrough) // h(Foo, { bar: 1, foo: 'ok', class: 'extra' }) // // @ts-expect-error should fail on missing required prop // h(Foo, {}) // // @ts-expect-error // h(Foo, { foo: 'ok' }) // // @ts-expect-error should fail on wrong type // h(Foo, { bar: 1, foo: 1 }) // }) // describe('h inference w/ defineComponent + direct function', () => { // const Foo = defineComponent((_props: { foo?: string; bar: number }) => {}) // h(Foo, { bar: 1 }) // h(Foo, { bar: 1, foo: 'ok' }) // // should allow extraneous props (attrs fallthrough) // h(Foo, { bar: 1, foo: 'ok', class: 'extra' }) // // @ts-expect-error should fail on missing required prop // h(Foo, {}) // // @ts-expect-error // h(Foo, { foo: 'ok' }) // // @ts-expect-error should fail on wrong type // h(Foo, { bar: 1, foo: 1 }) // }) // #922 and #3218 describe('h support for generic component type', () => { function foo(bar: Component) { h(bar) h(bar, 'hello') h(bar, { id: 'ok' }, 'hello') } foo({}) }) // #993 describe('describeComponent extends Component', () => { // functional expectAssignable( defineComponent((_props: { foo?: string; bar: number }) => () => {}), ) // typed props expectAssignable(defineComponent({})) // prop arrays expectAssignable( defineComponent({ props: ['a', 'b'], }), ) // prop object expectAssignable( defineComponent({ props: { foo: String, bar: { type: Number, required: true, }, }, }), ) }) // #1385 describe('component w/ props w/ default value', () => { const MyComponent = defineComponent({ props: { message: { type: String, default: 'hello', }, }, }) h(MyComponent, {}) }) // #2338 describe('Boolean prop implicit false', () => { const MyComponent = defineComponent({ props: { visible: Boolean, }, }) h(MyComponent, {}) const RequiredComponent = defineComponent({ props: { visible: { type: Boolean, required: true, }, }, }) h(RequiredComponent, { visible: true, }) // @ts-expect-error h(RequiredComponent, {}) }) // #2357 describe('resolveComponent should work', () => { h(resolveComponent('test')) h(resolveComponent('test'), { message: '1', }) }) // #5431 describe('h should work with multiple types', () => { const serializers = { Paragraph: 'p', Component: {} as Component, DefineComponent: {} as DefineComponent, } const sampleComponent = serializers['' as keyof typeof serializers] h(sampleComponent) h(sampleComponent, {}) h(sampleComponent, {}, []) })