default: 0
}
},
- render() {
- return this.count
+ setup(props: { count: number }) {
+ return () => props.count
}
}
-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'
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
this.$props.msg
this.msg
this.a * 2
- // return h('div', this.msg)
+ return h('div', this.msg)
}
})
;(<Comp msg="hello"/>)
}
},
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: {
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)
}
})
})
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
this.foo
)
}
- }
+ })
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(serializeInner(root)).toBe(`<div>1</div>`)
})
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
}
},
this.bar + this.baz
)
}
- }
+ })
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(serializeInner(root)).toBe(`<div>5</div>`)
})
test('methods', async () => {
- const Comp = {
+ const Comp = createComponent({
data() {
return {
foo: 1
this.foo
)
}
- }
+ })
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(serializeInner(root)).toBe(`<div>1</div>`)
})
test('watch', async () => {
- function returnThis() {
+ function returnThis(this: any) {
return this
}
const spyA = jest.fn(returnThis)
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: {
render() {
return this.b
}
- }
+ } as any
const ChildD = {
inject: {
b: {
render() {
return this.b
}
- }
+ } as any
expect(renderToString(h(Root))).toBe(`<!---->1112<!---->`)
})
unmounted() {
calls.push('mid onUnmounted')
},
- render() {
+ render(this: any) {
return h(Child, { count: this.$props.count })
}
}
unmounted() {
calls.push('child onUnmounted')
},
- render() {
+ render(this: any) {
return h('div', this.$props.count)
}
}
a: 1
}
},
- created() {
+ created(this: any) {
calls.push('mixinA created')
expect(this.a).toBe(1)
expect(this.b).toBe(2)
b: 2
}
},
- created() {
+ created(this: any) {
calls.push('mixinB created')
expect(this.a).toBe(1)
expect(this.b).toBe(2)
c: 3
}
},
- created() {
+ created(this: any) {
calls.push('comp created')
expect(this.a).toBe(1)
expect(this.b).toBe(2)
mounted() {
calls.push('comp mounted')
},
- render() {
+ render(this: any) {
return `${this.a}${this.b}${this.c}`
}
}
mounted() {
calls.push('comp')
},
- render() {
+ render(this: any) {
return `${this.a}${this.b}`
}
}
})
test('accessing setup() state from options', async () => {
- const Comp = {
+ const Comp = createComponent({
setup() {
return {
count: ref(0)
},
data() {
return {
- plusOne: this.count + 1
+ plusOne: (this as any).count + 1
}
},
computed: {
- plusTwo() {
+ plusTwo(): number {
return this.count + 2
}
},
`${this.count},${this.plusOne},${this.plusTwo}`
)
}
- }
+ })
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(serializeInner(root)).toBe(`<div>0,1,2</div>`)
describe('api: setup context', () => {
it('should expose return values to template render context', () => {
- const Comp = {
+ const Comp = createComponent({
setup() {
return {
// ref should auto-unwrap
render() {
return `${this.ref} ${this.object.msg} ${this.value}`
}
- }
+ })
expect(renderToString(h(Comp))).toMatch(`foo bar baz`)
})
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<
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
Ref,
ComputedRef,
UnwrapRef,
- ComputedOptions
+ WritableComputedOptions
} from '@vue/reactivity'
import {
Ref,
computed as _computed,
ComputedRef,
- ComputedOptions,
+ WritableComputedOptions,
ReactiveEffect
} from '@vue/reactivity'
}
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)
// in templates (as `this` in the render option)
export type ComponentRenderProxy<
P = {},
- D = {},
B = {},
+ D = {},
C = {},
M = {},
PublicProps = P
$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,
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
emit: ((event: string, ...args: unknown[]) => void)
}
+type RenderFunction = () => VNodeChild
+
export type ComponentInstance<P = Data, D = Data> = {
type: FunctionalComponent | ComponentOptions
parent: ComponentInstance | null
next: VNode | null
subTree: VNode
update: ReactiveEffect
- render: RenderFunction<P, D> | null
+ render: RenderFunction | null
effects: ReactiveEffect[] | null
provides: Data
>(
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 }
): {
new (): ComponentRenderProxy<
{ [key in PropNames]?: unknown },
- D,
RawBindings,
+ D,
C,
M
>
// for Vetur and TSX support
new (): ComponentRenderProxy<
ExtractPropTypes<PropsOptions>,
- D,
RawBindings,
+ D,
C,
M,
ExtractPropTypes<PropsOptions, false>
import { RawSlots } from './componentSlots'
import {
FunctionalComponent,
- ComponentOptions,
ComponentOptionsWithoutProps,
ComponentOptionsWithArrayProps,
- ComponentOptionsWithProps
+ ComponentOptionsWithProps,
+ ComponentOptions
} from './component'
import { ExtractPropTypes } from './componentProps'
[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> {
| 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