feat: fix all cases for h and options type inference
This commit is contained in:
parent
6c7cbb0dc9
commit
94a05561f8
@ -23,8 +23,8 @@ describe('api: createApp', () => {
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return this.count
|
||||
setup(props: { count: number }) {
|
||||
return () => props.count
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createComponent, ComponentRenderProxy } from '../src/component'
|
||||
import { createComponent } from '../src/component'
|
||||
import { ref } from '@vue/reactivity'
|
||||
import { PropType } from '../src/componentProps'
|
||||
import { h } from '../src/h'
|
||||
@ -56,7 +56,7 @@ test('createComponent type inference', () => {
|
||||
this.d.e.slice()
|
||||
this.cc && this.cc.push('hoo')
|
||||
this.dd.push('dd')
|
||||
// return h('div', this.bb)
|
||||
return h('div', this.bb)
|
||||
}
|
||||
})
|
||||
// test TSX props inference
|
||||
@ -75,7 +75,7 @@ test('type inference w/ optional props declaration', () => {
|
||||
this.$props.msg
|
||||
this.msg
|
||||
this.a * 2
|
||||
// return h('div', this.msg)
|
||||
return h('div', this.msg)
|
||||
}
|
||||
})
|
||||
;(<Comp msg="hello"/>)
|
||||
@ -118,10 +118,10 @@ test('with legacy options', () => {
|
||||
}
|
||||
},
|
||||
data() {
|
||||
this.a
|
||||
this.b
|
||||
// Limitation: we cannot expose the return result of setup() on `this`
|
||||
// here in data() - somehow that would mess up the inference
|
||||
return {
|
||||
c: 234
|
||||
c: this.a || 123
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -148,6 +148,13 @@ test('with legacy options', () => {
|
||||
this.d * 2
|
||||
return (this.a || 0) + this.b + this.c + this.d
|
||||
}
|
||||
},
|
||||
render() {
|
||||
this.a && this.a * 2
|
||||
this.b * 2
|
||||
this.c * 2
|
||||
this.d * 2
|
||||
return h('div', (this.a || 0) + this.b + this.c + this.d)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -7,12 +7,13 @@ import {
|
||||
TestElement,
|
||||
nextTick,
|
||||
renderToString,
|
||||
ref
|
||||
ref,
|
||||
createComponent
|
||||
} from '@vue/runtime-test'
|
||||
|
||||
describe('api: options', () => {
|
||||
test('data', async () => {
|
||||
const Comp = {
|
||||
const Comp = createComponent({
|
||||
data() {
|
||||
return {
|
||||
foo: 1
|
||||
@ -29,7 +30,7 @@ describe('api: options', () => {
|
||||
this.foo
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(Comp), root)
|
||||
expect(serializeInner(root)).toBe(`<div>1</div>`)
|
||||
@ -40,17 +41,17 @@ describe('api: options', () => {
|
||||
})
|
||||
|
||||
test('computed', async () => {
|
||||
const Comp = {
|
||||
const Comp = createComponent({
|
||||
data() {
|
||||
return {
|
||||
foo: 1
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
bar() {
|
||||
bar(): number {
|
||||
return this.foo + 1
|
||||
},
|
||||
baz() {
|
||||
baz(): number {
|
||||
return this.bar + 1
|
||||
}
|
||||
},
|
||||
@ -65,7 +66,7 @@ describe('api: options', () => {
|
||||
this.bar + this.baz
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(Comp), root)
|
||||
expect(serializeInner(root)).toBe(`<div>5</div>`)
|
||||
@ -76,7 +77,7 @@ describe('api: options', () => {
|
||||
})
|
||||
|
||||
test('methods', async () => {
|
||||
const Comp = {
|
||||
const Comp = createComponent({
|
||||
data() {
|
||||
return {
|
||||
foo: 1
|
||||
@ -96,7 +97,7 @@ describe('api: options', () => {
|
||||
this.foo
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(Comp), root)
|
||||
expect(serializeInner(root)).toBe(`<div>1</div>`)
|
||||
@ -107,7 +108,7 @@ describe('api: options', () => {
|
||||
})
|
||||
|
||||
test('watch', async () => {
|
||||
function returnThis() {
|
||||
function returnThis(this: any) {
|
||||
return this
|
||||
}
|
||||
const spyA = jest.fn(returnThis)
|
||||
@ -188,20 +189,20 @@ describe('api: options', () => {
|
||||
render() {
|
||||
return [h(ChildA), h(ChildB), h(ChildC), h(ChildD)]
|
||||
}
|
||||
}
|
||||
} as any
|
||||
const ChildA = {
|
||||
inject: ['a'],
|
||||
render() {
|
||||
return this.a
|
||||
}
|
||||
}
|
||||
} as any
|
||||
const ChildB = {
|
||||
// object alias
|
||||
inject: { b: 'a' },
|
||||
render() {
|
||||
return this.b
|
||||
}
|
||||
}
|
||||
} as any
|
||||
const ChildC = {
|
||||
inject: {
|
||||
b: {
|
||||
@ -211,7 +212,7 @@ describe('api: options', () => {
|
||||
render() {
|
||||
return this.b
|
||||
}
|
||||
}
|
||||
} as any
|
||||
const ChildD = {
|
||||
inject: {
|
||||
b: {
|
||||
@ -222,7 +223,7 @@ describe('api: options', () => {
|
||||
render() {
|
||||
return this.b
|
||||
}
|
||||
}
|
||||
} as any
|
||||
|
||||
expect(renderToString(h(Root))).toBe(`<!---->1112<!---->`)
|
||||
})
|
||||
@ -287,7 +288,7 @@ describe('api: options', () => {
|
||||
unmounted() {
|
||||
calls.push('mid onUnmounted')
|
||||
},
|
||||
render() {
|
||||
render(this: any) {
|
||||
return h(Child, { count: this.$props.count })
|
||||
}
|
||||
}
|
||||
@ -317,7 +318,7 @@ describe('api: options', () => {
|
||||
unmounted() {
|
||||
calls.push('child onUnmounted')
|
||||
},
|
||||
render() {
|
||||
render(this: any) {
|
||||
return h('div', this.$props.count)
|
||||
}
|
||||
}
|
||||
@ -375,7 +376,7 @@ describe('api: options', () => {
|
||||
a: 1
|
||||
}
|
||||
},
|
||||
created() {
|
||||
created(this: any) {
|
||||
calls.push('mixinA created')
|
||||
expect(this.a).toBe(1)
|
||||
expect(this.b).toBe(2)
|
||||
@ -391,7 +392,7 @@ describe('api: options', () => {
|
||||
b: 2
|
||||
}
|
||||
},
|
||||
created() {
|
||||
created(this: any) {
|
||||
calls.push('mixinB created')
|
||||
expect(this.a).toBe(1)
|
||||
expect(this.b).toBe(2)
|
||||
@ -408,7 +409,7 @@ describe('api: options', () => {
|
||||
c: 3
|
||||
}
|
||||
},
|
||||
created() {
|
||||
created(this: any) {
|
||||
calls.push('comp created')
|
||||
expect(this.a).toBe(1)
|
||||
expect(this.b).toBe(2)
|
||||
@ -417,7 +418,7 @@ describe('api: options', () => {
|
||||
mounted() {
|
||||
calls.push('comp mounted')
|
||||
},
|
||||
render() {
|
||||
render(this: any) {
|
||||
return `${this.a}${this.b}${this.c}`
|
||||
}
|
||||
}
|
||||
@ -455,7 +456,7 @@ describe('api: options', () => {
|
||||
mounted() {
|
||||
calls.push('comp')
|
||||
},
|
||||
render() {
|
||||
render(this: any) {
|
||||
return `${this.a}${this.b}`
|
||||
}
|
||||
}
|
||||
@ -465,7 +466,7 @@ describe('api: options', () => {
|
||||
})
|
||||
|
||||
test('accessing setup() state from options', async () => {
|
||||
const Comp = {
|
||||
const Comp = createComponent({
|
||||
setup() {
|
||||
return {
|
||||
count: ref(0)
|
||||
@ -473,11 +474,11 @@ describe('api: options', () => {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
plusOne: this.count + 1
|
||||
plusOne: (this as any).count + 1
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
plusTwo() {
|
||||
plusTwo(): number {
|
||||
return this.count + 2
|
||||
}
|
||||
},
|
||||
@ -495,7 +496,7 @@ describe('api: options', () => {
|
||||
`${this.count},${this.plusOne},${this.plusTwo}`
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(Comp), root)
|
||||
expect(serializeInner(root)).toBe(`<div>0,1,2</div>`)
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
|
||||
describe('api: setup context', () => {
|
||||
it('should expose return values to template render context', () => {
|
||||
const Comp = {
|
||||
const Comp = createComponent({
|
||||
setup() {
|
||||
return {
|
||||
// ref should auto-unwrap
|
||||
@ -30,7 +30,7 @@ describe('api: setup context', () => {
|
||||
render() {
|
||||
return `${this.ref} ${this.object.msg} ${this.value}`
|
||||
}
|
||||
}
|
||||
})
|
||||
expect(renderToString(h(Comp))).toMatch(`foo bar baz`)
|
||||
})
|
||||
|
||||
|
@ -66,30 +66,20 @@ export interface LegacyOptions<
|
||||
RawBindings,
|
||||
D,
|
||||
C extends ComputedOptions,
|
||||
M extends MethodOptions,
|
||||
ThisContext = ThisType<ComponentRenderProxy<Props, D, RawBindings, C, M>>
|
||||
M extends MethodOptions
|
||||
> {
|
||||
el?: any
|
||||
|
||||
// state
|
||||
data?:
|
||||
| D
|
||||
| (<This extends ComponentRenderProxy<Props, {}, RawBindings>>(
|
||||
this: This
|
||||
) => D)
|
||||
computed?: C & ThisContext
|
||||
methods?: M & ThisContext
|
||||
data?: D | ((this: ComponentRenderProxy<Props>) => D)
|
||||
computed?: C
|
||||
methods?: M
|
||||
// TODO watch array
|
||||
watch?: Record<
|
||||
string,
|
||||
string | WatchHandler | { handler: WatchHandler } & WatchOptions
|
||||
> &
|
||||
ThisContext
|
||||
provide?:
|
||||
| Data
|
||||
| (<This extends ComponentRenderProxy<Props, D, RawBindings, C, M>>(
|
||||
this: This
|
||||
) => any)
|
||||
>
|
||||
provide?: Data | Function
|
||||
inject?:
|
||||
| string[]
|
||||
| Record<
|
||||
@ -102,10 +92,8 @@ export interface LegacyOptions<
|
||||
extends?: LegacyComponent
|
||||
|
||||
// lifecycle
|
||||
beforeCreate?(this: ComponentRenderProxy): void
|
||||
created?<This extends ComponentRenderProxy<Props, D, RawBindings, C, M>>(
|
||||
this: This
|
||||
): void
|
||||
beforeCreate?(): void
|
||||
created?(): void
|
||||
beforeMount?(): void
|
||||
mounted?(): void
|
||||
beforeUpdate?(): void
|
||||
|
@ -18,14 +18,14 @@ export {
|
||||
Ref,
|
||||
ComputedRef,
|
||||
UnwrapRef,
|
||||
ComputedOptions
|
||||
WritableComputedOptions
|
||||
} from '@vue/reactivity'
|
||||
|
||||
import {
|
||||
Ref,
|
||||
computed as _computed,
|
||||
ComputedRef,
|
||||
ComputedOptions,
|
||||
WritableComputedOptions,
|
||||
ReactiveEffect
|
||||
} from '@vue/reactivity'
|
||||
|
||||
@ -40,7 +40,7 @@ export function recordEffect(effect: ReactiveEffect) {
|
||||
}
|
||||
|
||||
export function computed<T>(getter: () => T): ComputedRef<T>
|
||||
export function computed<T>(options: ComputedOptions<T>): Ref<T>
|
||||
export function computed<T>(options: WritableComputedOptions<T>): Ref<T>
|
||||
export function computed<T>(getterOrOptions: any) {
|
||||
const c = _computed(getterOrOptions)
|
||||
recordEffect(c.effect)
|
||||
|
@ -37,8 +37,8 @@ export type Data = { [key: string]: unknown }
|
||||
// in templates (as `this` in the render option)
|
||||
export type ComponentRenderProxy<
|
||||
P = {},
|
||||
D = {},
|
||||
B = {},
|
||||
D = {},
|
||||
C = {},
|
||||
M = {},
|
||||
PublicProps = P
|
||||
@ -52,17 +52,11 @@ export type ComponentRenderProxy<
|
||||
$parent: ComponentInstance | null
|
||||
$emit: (event: string, ...args: unknown[]) => void
|
||||
} & P &
|
||||
D &
|
||||
UnwrapRef<B> &
|
||||
D &
|
||||
ExtracComputedReturns<C> &
|
||||
M
|
||||
|
||||
type RenderFunction<P = {}, D = {}, B = {}, C = {}, M = {}> = <
|
||||
This extends ComponentRenderProxy<P, D, B, C, M>
|
||||
>(
|
||||
this: This
|
||||
) => VNodeChild
|
||||
|
||||
interface ComponentOptionsBase<
|
||||
Props,
|
||||
RawBindings,
|
||||
@ -71,47 +65,53 @@ interface ComponentOptionsBase<
|
||||
M extends MethodOptions
|
||||
> extends LegacyOptions<Props, RawBindings, D, C, M> {
|
||||
setup?: (
|
||||
this: null,
|
||||
props: Props,
|
||||
ctx: SetupContext
|
||||
) => RawBindings | (() => VNodeChild) | void
|
||||
name?: string
|
||||
template?: string
|
||||
render?: RenderFunction<Props, D, RawBindings, C, M>
|
||||
// Note: we are intentionally using the signature-less `Function` type here
|
||||
// since any type with signature will cause the whole inference to fail when
|
||||
// the return expression contains reference to `this`.
|
||||
// Luckily `render()` doesn't need any arguments nor does it care about return
|
||||
// type.
|
||||
render?: Function
|
||||
components?: Record<string, Component>
|
||||
directives?: Record<string, Directive>
|
||||
}
|
||||
|
||||
export interface ComponentOptionsWithoutProps<
|
||||
export type ComponentOptionsWithoutProps<
|
||||
Props = {},
|
||||
RawBindings = {},
|
||||
D = {},
|
||||
C extends ComputedOptions = {},
|
||||
M extends MethodOptions = {}
|
||||
> extends ComponentOptionsBase<Props, RawBindings, D, C, M> {
|
||||
> = ComponentOptionsBase<Props, RawBindings, D, C, M> & {
|
||||
props?: undefined
|
||||
}
|
||||
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
|
||||
|
||||
export interface ComponentOptionsWithArrayProps<
|
||||
export type ComponentOptionsWithArrayProps<
|
||||
PropNames extends string = string,
|
||||
RawBindings = {},
|
||||
D = {},
|
||||
C extends ComputedOptions = {},
|
||||
M extends MethodOptions = {},
|
||||
Props = { [key in PropNames]?: unknown }
|
||||
> extends ComponentOptionsBase<Props, RawBindings, D, C, M> {
|
||||
> = ComponentOptionsBase<Props, RawBindings, D, C, M> & {
|
||||
props: PropNames[]
|
||||
}
|
||||
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
|
||||
|
||||
export interface ComponentOptionsWithProps<
|
||||
export type ComponentOptionsWithProps<
|
||||
PropsOptions = ComponentPropsOptions,
|
||||
RawBindings = {},
|
||||
D = {},
|
||||
C extends ComputedOptions = {},
|
||||
M extends MethodOptions = {},
|
||||
Props = ExtractPropTypes<PropsOptions>
|
||||
> extends ComponentOptionsBase<Props, RawBindings, D, C, M> {
|
||||
> = ComponentOptionsBase<Props, RawBindings, D, C, M> & {
|
||||
props: PropsOptions
|
||||
}
|
||||
} & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
|
||||
|
||||
export type ComponentOptions =
|
||||
| ComponentOptionsWithoutProps
|
||||
@ -150,6 +150,8 @@ interface SetupContext {
|
||||
emit: ((event: string, ...args: unknown[]) => void)
|
||||
}
|
||||
|
||||
type RenderFunction = () => VNodeChild
|
||||
|
||||
export type ComponentInstance<P = Data, D = Data> = {
|
||||
type: FunctionalComponent | ComponentOptions
|
||||
parent: ComponentInstance | null
|
||||
@ -159,7 +161,7 @@ export type ComponentInstance<P = Data, D = Data> = {
|
||||
next: VNode | null
|
||||
subTree: VNode
|
||||
update: ReactiveEffect
|
||||
render: RenderFunction<P, D> | null
|
||||
render: RenderFunction | null
|
||||
effects: ReactiveEffect[] | null
|
||||
provides: Data
|
||||
|
||||
@ -211,7 +213,7 @@ export function createComponent<
|
||||
>(
|
||||
options: ComponentOptionsWithoutProps<Props, RawBindings, D, C, M>
|
||||
): {
|
||||
new (): ComponentRenderProxy<Props, D, RawBindings, C, M>
|
||||
new (): ComponentRenderProxy<Props, RawBindings, D, C, M>
|
||||
}
|
||||
// overload 3: object format with array props declaration
|
||||
// props inferred as { [key in PropNames]?: unknown }
|
||||
@ -227,8 +229,8 @@ export function createComponent<
|
||||
): {
|
||||
new (): ComponentRenderProxy<
|
||||
{ [key in PropNames]?: unknown },
|
||||
D,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M
|
||||
>
|
||||
@ -247,8 +249,8 @@ export function createComponent<
|
||||
// for Vetur and TSX support
|
||||
new (): ComponentRenderProxy<
|
||||
ExtractPropTypes<PropsOptions>,
|
||||
D,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
ExtractPropTypes<PropsOptions, false>
|
||||
|
@ -11,10 +11,10 @@ import { Ref } from '@vue/reactivity'
|
||||
import { RawSlots } from './componentSlots'
|
||||
import {
|
||||
FunctionalComponent,
|
||||
ComponentOptions,
|
||||
ComponentOptionsWithoutProps,
|
||||
ComponentOptionsWithArrayProps,
|
||||
ComponentOptionsWithProps
|
||||
ComponentOptionsWithProps,
|
||||
ComponentOptions
|
||||
} from './component'
|
||||
import { ExtractPropTypes } from './componentProps'
|
||||
|
||||
@ -57,7 +57,7 @@ interface Props {
|
||||
[Symbol.iterator]?: never
|
||||
}
|
||||
|
||||
type Children = string | number | VNodeChildren
|
||||
type Children = string | number | boolean | VNodeChildren | (() => any)
|
||||
|
||||
// fake constructor type returned from `createComponent`
|
||||
interface Constructor<P = any> {
|
||||
|
@ -28,7 +28,7 @@ export type VNodeTypes =
|
||||
| typeof Text
|
||||
| typeof Empty
|
||||
|
||||
type VNodeChildAtom = VNode | string | number | null | void
|
||||
type VNodeChildAtom = VNode | string | number | boolean | null | void
|
||||
export interface VNodeChildren extends Array<VNodeChildren | VNodeChildAtom> {}
|
||||
export type VNodeChild = VNodeChildAtom | VNodeChildren
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user