From b8c1be18f364dda5b9513a62cb4562125e8ce249 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 23 Mar 2020 11:08:22 -0400 Subject: [PATCH] refactor(types): use stricter settings fix #847 --- packages/compiler-core/src/transforms/vOn.ts | 8 +- .../src/templateTransformAssetUrl.ts | 3 +- packages/reactivity/src/effect.ts | 2 +- .../__tests__/apiCreateApp.spec.ts | 15 +- .../components/BaseTransition.spec.ts | 23 +- .../runtime-core/__tests__/hydration.spec.ts | 12 +- packages/runtime-core/src/apiCreateApp.ts | 15 +- packages/runtime-core/src/apiOptions.ts | 20 +- packages/runtime-core/src/apiWatch.ts | 2 +- packages/runtime-core/src/component.ts | 7 +- .../runtime-core/src/componentRenderUtils.ts | 2 +- .../src/components/BaseTransition.ts | 35 +- .../runtime-core/src/components/KeepAlive.ts | 10 +- .../runtime-core/src/components/Portal.ts | 11 +- .../runtime-core/src/components/Suspense.ts | 77 ++-- .../src/helpers/withRenderContext.ts | 2 +- packages/runtime-core/src/renderer.ts | 390 +++++++++--------- packages/runtime-core/src/vnode.ts | 40 +- .../runtime-dom/src/components/Transition.ts | 28 +- .../src/components/TransitionGroup.ts | 29 +- packages/runtime-dom/src/directives/vModel.ts | 4 +- packages/runtime-dom/src/directives/vOn.ts | 4 +- packages/runtime-dom/src/index.ts | 2 +- packages/runtime-test/src/index.ts | 4 +- packages/vue/examples/__tests__/e2eUtils.ts | 15 +- .../vue/examples/__tests__/markdown.spec.ts | 2 +- tsconfig.json | 4 +- 27 files changed, 385 insertions(+), 381 deletions(-) diff --git a/packages/compiler-core/src/transforms/vOn.ts b/packages/compiler-core/src/transforms/vOn.ts index 487db392..019fcbd5 100644 --- a/packages/compiler-core/src/transforms/vOn.ts +++ b/packages/compiler-core/src/transforms/vOn.ts @@ -26,12 +26,12 @@ export interface VOnDirectiveNode extends DirectiveNode { } export const transformOn: DirectiveTransform = ( - dir: VOnDirectiveNode, + dir, node, context, augmentor ) => { - const { loc, modifiers, arg } = dir + const { loc, modifiers, arg } = dir as VOnDirectiveNode if (!dir.exp && !modifiers.length) { context.onError(createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc)) } @@ -55,7 +55,9 @@ export const transformOn: DirectiveTransform = ( } // handler processing - let exp: ExpressionNode | undefined = dir.exp + let exp: ExpressionNode | undefined = dir.exp as + | SimpleExpressionNode + | undefined if (exp && !exp.content.trim()) { exp = undefined } diff --git a/packages/compiler-sfc/src/templateTransformAssetUrl.ts b/packages/compiler-sfc/src/templateTransformAssetUrl.ts index e6b0c498..3232c15b 100644 --- a/packages/compiler-sfc/src/templateTransformAssetUrl.ts +++ b/packages/compiler-sfc/src/templateTransformAssetUrl.ts @@ -1,5 +1,4 @@ import { - AttributeNode, createSimpleExpression, ExpressionNode, NodeTransform, @@ -42,7 +41,7 @@ export const transformAssetUrl: NodeTransform = ( if ((tag === '*' || node.tag === tag) && node.props.length) { const attributes = options[tag] attributes.forEach(item => { - node.props.forEach((attr: AttributeNode, index) => { + node.props.forEach((attr, index) => { if (attr.type !== NodeTypes.ATTRIBUTE) return if (attr.name !== item) return if (!attr.value) return diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 7b8ec4d4..abce4a1e 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -21,7 +21,7 @@ export interface ReactiveEffect { export interface ReactiveEffectOptions { lazy?: boolean computed?: boolean - scheduler?: (run: Function) => void + scheduler?: (job: () => void) => void onTrack?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerEvent) => void onStop?: () => void diff --git a/packages/runtime-core/__tests__/apiCreateApp.spec.ts b/packages/runtime-core/__tests__/apiCreateApp.spec.ts index c80f005a..d43b660d 100644 --- a/packages/runtime-core/__tests__/apiCreateApp.spec.ts +++ b/packages/runtime-core/__tests__/apiCreateApp.spec.ts @@ -10,7 +10,8 @@ import { withDirectives, Plugin, ref, - getCurrentInstance + getCurrentInstance, + defineComponent } from '@vue/runtime-test' import { mockWarn } from '@vue/shared' @@ -18,16 +19,16 @@ describe('api: createApp', () => { mockWarn() test('mount', () => { - const Comp = { + const Comp = defineComponent({ props: { count: { default: 0 } }, - setup(props: { count: number }) { + setup(props) { return () => props.count } - } + }) const root1 = nodeOps.createElement('div') createApp(Comp).mount(root1) @@ -47,16 +48,16 @@ describe('api: createApp', () => { }) test('unmount', () => { - const Comp = { + const Comp = defineComponent({ props: { count: { default: 0 } }, - setup(props: { count: number }) { + setup(props) { return () => props.count } - } + }) const root = nodeOps.createElement('div') const app = createApp(Comp) diff --git a/packages/runtime-core/__tests__/components/BaseTransition.spec.ts b/packages/runtime-core/__tests__/components/BaseTransition.spec.ts index 6c204a47..dc0d1e0a 100644 --- a/packages/runtime-core/__tests__/components/BaseTransition.spec.ts +++ b/packages/runtime-core/__tests__/components/BaseTransition.spec.ts @@ -9,7 +9,8 @@ import { serializeInner, serialize, VNodeProps, - KeepAlive + KeepAlive, + TestElement } from '@vue/runtime-test' function mount( @@ -42,13 +43,13 @@ function mockProps(extra: BaseTransitionProps = {}, withKeepAlive = false) { } }), onEnter: jest.fn((el, done) => { - cbs.doneEnter[serialize(el)] = done + cbs.doneEnter[serialize(el as TestElement)] = done }), onAfterEnter: jest.fn(), onEnterCancelled: jest.fn(), onBeforeLeave: jest.fn(), onLeave: jest.fn((el, done) => { - cbs.doneLeave[serialize(el)] = done + cbs.doneLeave[serialize(el as TestElement)] = done }), onAfterLeave: jest.fn(), onLeaveCancelled: jest.fn(), @@ -64,8 +65,10 @@ function assertCalls( props: BaseTransitionProps, calls: Record ) { - Object.keys(calls).forEach((key: keyof BaseTransitionProps) => { - expect(props[key]).toHaveBeenCalledTimes(calls[key]) + Object.keys(calls).forEach(key => { + expect(props[key as keyof BaseTransitionProps]).toHaveBeenCalledTimes( + calls[key] + ) }) } @@ -147,19 +150,19 @@ describe('BaseTransition', () => { const toggle = ref(true) const hooks: VNodeProps = { onVnodeBeforeMount(vnode) { - vnode.transition!.beforeEnter(vnode.el) + vnode.transition!.beforeEnter(vnode.el!) }, onVnodeMounted(vnode) { - vnode.transition!.enter(vnode.el) + vnode.transition!.enter(vnode.el!) }, onVnodeUpdated(vnode, oldVnode) { if (oldVnode.props!.id !== vnode.props!.id) { if (vnode.props!.id) { - vnode.transition!.beforeEnter(vnode.el) + vnode.transition!.beforeEnter(vnode.el!) state.show = true - vnode.transition!.enter(vnode.el) + vnode.transition!.enter(vnode.el!) } else { - vnode.transition!.leave(vnode.el, () => { + vnode.transition!.leave(vnode.el!, () => { state.show = false }) } diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index 0720eef6..fb08d744 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -19,7 +19,9 @@ function mountWithHydration(html: string, render: () => any) { render }) return { - vnode: app.mount(container).$.subTree, + vnode: app.mount(container).$.subTree as VNode & { + el: Element + }, container } } @@ -90,7 +92,7 @@ describe('SSR hydration', () => { ) // event handler - triggerEvent('click', vnode.el.querySelector('.foo')) + triggerEvent('click', vnode.el.querySelector('.foo')!) expect(fn).toHaveBeenCalled() msg.value = 'bar' @@ -120,7 +122,7 @@ describe('SSR hydration', () => { const fragment1Children = fragment1.children as VNode[] // first - expect(fragment1Children[0].el.tagName).toBe('SPAN') + expect(fragment1Children[0].el!.tagName).toBe('SPAN') expect(fragment1Children[0].el).toBe(vnode.el.childNodes[1]) // start fragment 2 @@ -129,7 +131,7 @@ describe('SSR hydration', () => { const fragment2Children = fragment2.children as VNode[] // second - expect(fragment2Children[0].el.tagName).toBe('SPAN') + expect(fragment2Children[0].el!.tagName).toBe('SPAN') expect(fragment2Children[0].el).toBe(vnode.el.childNodes[3]) // end fragment 2 @@ -139,7 +141,7 @@ describe('SSR hydration', () => { expect(fragment1.anchor).toBe(vnode.el.childNodes[5]) // event handler - triggerEvent('click', vnode.el.querySelector('.foo')) + triggerEvent('click', vnode.el.querySelector('.foo')!) expect(fn).toHaveBeenCalled() msg.value = 'bar' diff --git a/packages/runtime-core/src/apiCreateApp.ts b/packages/runtime-core/src/apiCreateApp.ts index 4f710511..3c032503 100644 --- a/packages/runtime-core/src/apiCreateApp.ts +++ b/packages/runtime-core/src/apiCreateApp.ts @@ -12,6 +12,7 @@ import { InjectionKey } from './apiInject' import { isFunction, NO, isObject } from '@vue/shared' import { warn } from './warning' import { createVNode, cloneVNode, VNode } from './vnode' +import { RootHydrateFunction } from './hydration' export interface App { config: AppConfig @@ -91,11 +92,11 @@ export type CreateAppFunction = ( rootProps?: Data | null ) => App -export function createAppAPI( - render: RootRenderFunction, - hydrate?: (vnode: VNode, container: Element) => void +export function createAppAPI( + render: RootRenderFunction, + hydrate?: RootHydrateFunction ): CreateAppFunction { - return function createApp(rootComponent: Component, rootProps = null) { + return function createApp(rootComponent, rootProps = null) { if (rootProps != null && !isObject(rootProps)) { __DEV__ && warn(`root props passed to app.mount() must be an object.`) rootProps = null @@ -107,7 +108,7 @@ export function createAppAPI( let isMounted = false const app: App = { - _component: rootComponent, + _component: rootComponent as Component, _props: rootProps, _container: null, _context: context, @@ -189,7 +190,7 @@ export function createAppAPI( mount(rootContainer: HostElement, isHydrate?: boolean): any { if (!isMounted) { - const vnode = createVNode(rootComponent, rootProps) + const vnode = createVNode(rootComponent as Component, rootProps) // store app context on the root VNode. // this will be set on the root instance on initial mount. vnode.appContext = context @@ -202,7 +203,7 @@ export function createAppAPI( } if (isHydrate && hydrate) { - hydrate(vnode, rootContainer as any) + hydrate(vnode as VNode, rootContainer as any) } else { render(vnode, rootContainer) } diff --git a/packages/runtime-core/src/apiOptions.ts b/packages/runtime-core/src/apiOptions.ts index dcb66766..9c369d9a 100644 --- a/packages/runtime-core/src/apiOptions.ts +++ b/packages/runtime-core/src/apiOptions.ts @@ -89,7 +89,7 @@ export type ComponentOptionsWithoutProps< D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {} -> = ComponentOptionsBase, RawBindings, D, C, M> & { +> = ComponentOptionsBase & { props?: undefined } & ThisType>> @@ -116,12 +116,9 @@ export type ComponentOptionsWithObjectProps< } & ThisType> export type ComponentOptions = - | ComponentOptionsWithoutProps - | ComponentOptionsWithObjectProps - | ComponentOptionsWithArrayProps - -// TODO legacy component definition also supports constructors with .options -type LegacyComponent = ComponentOptions + | ComponentOptionsWithoutProps + | ComponentOptionsWithObjectProps + | ComponentOptionsWithArrayProps export type ComputedOptions = Record< string, @@ -167,7 +164,10 @@ export interface LegacyOptions< // Limitation: we cannot expose RawBindings on the `this` context for data // since that leads to some sort of circular inference and breaks ThisType // for the entire component. - data?: (this: ComponentPublicInstance) => D + data?: ( + this: ComponentPublicInstance, + vm: ComponentPublicInstance + ) => D computed?: C methods?: M watch?: ComponentWatchOptions @@ -175,8 +175,8 @@ export interface LegacyOptions< inject?: ComponentInjectOptions // composition - mixins?: LegacyComponent[] - extends?: LegacyComponent + mixins?: ComponentOptions[] + extends?: ComponentOptions // lifecycle beforeCreate?(): void diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 329b639c..9d477ac8 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -179,7 +179,7 @@ function doWatch( getter = () => traverse(baseGetter()) } - let cleanup: Function + let cleanup: () => void const onInvalidate: InvalidateCbRegistrator = (fn: () => void) => { cleanup = runner.options.onStop = () => { callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 3dbfd0d4..a346eb0c 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -63,7 +63,7 @@ export interface ClassComponent { __vccOpts: ComponentOptions } -export type Component = ComponentOptions | FunctionalComponent +export type Component = ComponentOptions | FunctionalComponent // A type used in public APIs where a component type is expected. // The constructor type is an artificial type returned by defineComponent(). @@ -100,7 +100,10 @@ export interface SetupContext { } export type RenderFunction = { - (): VNodeChild + ( + ctx: ComponentPublicInstance, + cache: ComponentInternalInstance['renderCache'] + ): VNodeChild _rc?: boolean // isRuntimeCompiled } diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index 571128b1..d20ba2b7 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -60,7 +60,7 @@ export function renderComponentRoot( // runtime-compiled render functions using `with` block. const proxyToUse = withProxy || proxy result = normalizeVNode( - instance.render!.call(proxyToUse, proxyToUse, renderCache) + instance.render!.call(proxyToUse, proxyToUse!, renderCache) ) } else { // functional diff --git a/packages/runtime-core/src/components/BaseTransition.ts b/packages/runtime-core/src/components/BaseTransition.ts index ffe5597a..f516ad92 100644 --- a/packages/runtime-core/src/components/BaseTransition.ts +++ b/packages/runtime-core/src/components/BaseTransition.ts @@ -17,8 +17,9 @@ import { toRaw } from '@vue/reactivity' import { callWithAsyncErrorHandling, ErrorCodes } from '../errorHandling' import { ShapeFlags } from '@vue/shared' import { onBeforeUnmount, onMounted } from '../apiLifecycle' +import { RendererElement } from '../renderer' -export interface BaseTransitionProps { +export interface BaseTransitionProps { mode?: 'in-out' | 'out-in' | 'default' appear?: boolean @@ -32,25 +33,25 @@ export interface BaseTransitionProps { // Hooks. Using camel case for easier usage in render functions & JSX. // In templates these can be written as @before-enter="xxx" as prop names // are camelized. - onBeforeEnter?: (el: any) => void - onEnter?: (el: any, done: () => void) => void - onAfterEnter?: (el: any) => void - onEnterCancelled?: (el: any) => void + onBeforeEnter?: (el: HostElement) => void + onEnter?: (el: HostElement, done: () => void) => void + onAfterEnter?: (el: HostElement) => void + onEnterCancelled?: (el: HostElement) => void // leave - onBeforeLeave?: (el: any) => void - onLeave?: (el: any, done: () => void) => void - onAfterLeave?: (el: any) => void - onLeaveCancelled?: (el: any) => void // only fired in persisted mode + onBeforeLeave?: (el: HostElement) => void + onLeave?: (el: HostElement, done: () => void) => void + onAfterLeave?: (el: HostElement) => void + onLeaveCancelled?: (el: HostElement) => void // only fired in persisted mode } export interface TransitionHooks { persisted: boolean - beforeEnter(el: object): void - enter(el: object): void - leave(el: object, remove: () => void): void + beforeEnter(el: RendererElement): void + enter(el: RendererElement): void + leave(el: RendererElement, remove: () => void): void afterLeave?(): void delayLeave?( - el: object, + el: RendererElement, earlyRemove: () => void, delayedLeave: () => void ): void @@ -222,7 +223,7 @@ if (__DEV__) { // also to avoid inline import() in generated d.ts files export const BaseTransition = (BaseTransitionImpl as any) as { new (): { - $props: BaseTransitionProps + $props: BaseTransitionProps } } @@ -254,7 +255,7 @@ export function resolveTransitionHooks( onLeave, onAfterLeave, onLeaveCancelled - }: BaseTransitionProps, + }: BaseTransitionProps, state: TransitionState, instance: ComponentInternalInstance ): TransitionHooks { @@ -286,10 +287,10 @@ export function resolveTransitionHooks( if ( leavingVNode && isSameVNodeType(vnode, leavingVNode) && - leavingVNode.el._leaveCb + leavingVNode.el!._leaveCb ) { // force early removal (not cancelled) - leavingVNode.el._leaveCb() + leavingVNode.el!._leaveCb() } callHook(onBeforeEnter, [el]) }, diff --git a/packages/runtime-core/src/components/KeepAlive.ts b/packages/runtime-core/src/components/KeepAlive.ts index c7ecb3e8..c66fd50d 100644 --- a/packages/runtime-core/src/components/KeepAlive.ts +++ b/packages/runtime-core/src/components/KeepAlive.ts @@ -17,7 +17,9 @@ import { RendererInternals, queuePostRenderEffect, invokeHooks, - MoveType + MoveType, + RendererElement, + RendererNode } from '../renderer' import { setTransitionHooks } from './BaseTransition' @@ -36,7 +38,11 @@ type Keys = Set export interface KeepAliveSink { renderer: RendererInternals parentSuspense: SuspenseBoundary | null - activate: (vnode: VNode, container: object, anchor: object | null) => void + activate: ( + vnode: VNode, + container: RendererElement, + anchor: RendererNode | null + ) => void deactivate: (vnode: VNode) => void } diff --git a/packages/runtime-core/src/components/Portal.ts b/packages/runtime-core/src/components/Portal.ts index 2a091cd3..46855ef7 100644 --- a/packages/runtime-core/src/components/Portal.ts +++ b/packages/runtime-core/src/components/Portal.ts @@ -1,6 +1,11 @@ import { ComponentInternalInstance } from '../component' import { SuspenseBoundary } from './Suspense' -import { RendererInternals, MoveType } from '../renderer' +import { + RendererInternals, + MoveType, + RendererElement, + RendererNode +} from '../renderer' import { VNode, VNodeArrayChildren, VNodeProps } from '../vnode' import { isString, ShapeFlags, PatchFlags } from '@vue/shared' import { warn } from '../warning' @@ -16,8 +21,8 @@ export const PortalImpl = { process( n1: VNode | null, n2: VNode, - container: object, - anchor: object | null, + container: RendererElement, + anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, isSVG: boolean, diff --git a/packages/runtime-core/src/components/Suspense.ts b/packages/runtime-core/src/components/Suspense.ts index a7dd7e01..4e925830 100644 --- a/packages/runtime-core/src/components/Suspense.ts +++ b/packages/runtime-core/src/components/Suspense.ts @@ -2,7 +2,13 @@ import { VNode, normalizeVNode, VNodeChild, VNodeProps } from '../vnode' import { isFunction, isArray, ShapeFlags } from '@vue/shared' import { ComponentInternalInstance, handleSetupResult } from '../component' import { Slots } from '../componentSlots' -import { RendererInternals, MoveType, SetupRenderEffectFn } from '../renderer' +import { + RendererInternals, + MoveType, + SetupRenderEffectFn, + RendererNode, + RendererElement +} from '../renderer' import { queuePostFlushCb, queueJob } from '../scheduler' import { updateHOCHostEl } from '../componentRenderUtils' import { pushWarningContext, popWarningContext } from '../warning' @@ -27,8 +33,8 @@ export const SuspenseImpl = { process( n1: VNode | null, n2: VNode, - container: object, - anchor: object | null, + container: RendererElement, + anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, isSVG: boolean, @@ -73,8 +79,8 @@ export const Suspense = ((__FEATURE_SUSPENSE__ function mountSuspense( n2: VNode, - container: object, - anchor: object | null, + container: RendererElement, + anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, isSVG: boolean, @@ -132,8 +138,8 @@ function mountSuspense( function patchSuspense( n1: VNode, n2: VNode, - container: object, - anchor: object | null, + container: RendererElement, + anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, isSVG: boolean, optimized: boolean, @@ -190,21 +196,17 @@ function patchSuspense( suspense.fallbackTree = fallback } -export interface SuspenseBoundary< - HostNode = any, - HostElement = any, - HostVNode = VNode -> { - vnode: HostVNode - parent: SuspenseBoundary | null +export interface SuspenseBoundary { + vnode: VNode + parent: SuspenseBoundary | null parentComponent: ComponentInternalInstance | null isSVG: boolean optimized: boolean - container: HostElement - hiddenContainer: HostElement - anchor: HostNode | null - subTree: HostVNode - fallbackTree: HostVNode + container: RendererElement + hiddenContainer: RendererElement + anchor: RendererNode | null + subTree: VNode + fallbackTree: VNode deps: number isHydrating: boolean isResolved: boolean @@ -212,30 +214,31 @@ export interface SuspenseBoundary< effects: Function[] resolve(): void recede(): void - move(container: HostElement, anchor: HostNode | null, type: MoveType): void - next(): HostNode | null + move( + container: RendererElement, + anchor: RendererNode | null, + type: MoveType + ): void + next(): RendererNode | null registerDep( instance: ComponentInternalInstance, - setupRenderEffect: SetupRenderEffectFn - ): void - unmount( - parentSuspense: SuspenseBoundary | null, - doRemove?: boolean + setupRenderEffect: SetupRenderEffectFn ): void + unmount(parentSuspense: SuspenseBoundary | null, doRemove?: boolean): void } -function createSuspenseBoundary( - vnode: VNode, - parent: SuspenseBoundary | null, +function createSuspenseBoundary( + vnode: VNode, + parent: SuspenseBoundary | null, parentComponent: ComponentInternalInstance | null, - container: HostElement, - hiddenContainer: HostElement, - anchor: HostNode | null, + container: RendererElement, + hiddenContainer: RendererElement, + anchor: RendererNode | null, isSVG: boolean, optimized: boolean, - rendererInternals: RendererInternals, + rendererInternals: RendererInternals, isHydrating = false -): SuspenseBoundary { +): SuspenseBoundary { const { p: patch, m: move, @@ -250,7 +253,7 @@ function createSuspenseBoundary( : suspense.fallbackTree const { content, fallback } = normalizeSuspenseChildren(vnode) - const suspense: SuspenseBoundary = { + const suspense: SuspenseBoundary = { vnode, parent, parentComponent, @@ -430,7 +433,7 @@ function createSuspenseBoundary( // placeholder. hydratedEl ? parentNode(hydratedEl)! - : parentNode(instance.subTree.el)!, + : parentNode(instance.subTree.el!)!, // anchor will not be used if this is hydration, so only need to // consider the comment placeholder case. hydratedEl ? null : next(instance.subTree), @@ -484,7 +487,7 @@ function hydrateSuspense( vnode, parentSuspense, parentComponent, - node.parentNode, + node.parentNode!, document.createElement('div'), null, isSVG, diff --git a/packages/runtime-core/src/helpers/withRenderContext.ts b/packages/runtime-core/src/helpers/withRenderContext.ts index 59c686b1..558d40d2 100644 --- a/packages/runtime-core/src/helpers/withRenderContext.ts +++ b/packages/runtime-core/src/helpers/withRenderContext.ts @@ -13,7 +13,7 @@ export function withCtx( return function renderFnWithContext() { const owner = currentRenderingInstance setCurrentRenderingInstance(ctx) - const res = fn.apply(null, arguments) + const res = fn.apply(null, arguments as any) setCurrentRenderingInstance(owner) return res } diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 942bedb5..abf00655 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -71,21 +71,24 @@ import { invokeDirectiveHook } from './directives' const __HMR__ = __BUNDLER__ && __DEV__ -export interface Renderer { - render: RootRenderFunction +export interface Renderer { + render: RootRenderFunction createApp: CreateAppFunction } -export interface HydrationRenderer extends Renderer { +export interface HydrationRenderer extends Renderer { hydrate: RootHydrateFunction } -export type RootRenderFunction = ( - vnode: VNode | null, +export type RootRenderFunction = ( + vnode: VNode | null, container: HostElement ) => void -export interface RendererOptions { +export interface RendererOptions< + HostNode = RendererNode, + HostElement = RendererElement +> { patchProp( el: HostElement, key: string, @@ -94,8 +97,8 @@ export interface RendererOptions { isSVG?: boolean, prevChildren?: VNode[], parentComponent?: ComponentInternalInstance | null, - parentSuspense?: SuspenseBoundary | null, - unmountChildren?: UnmountChildrenFn + parentSuspense?: SuspenseBoundary | null, + unmountChildren?: UnmountChildrenFn ): void insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void remove(el: HostNode): void @@ -117,115 +120,126 @@ export interface RendererOptions { ): HostElement } +// Renderer Node can technically be any object in the context of core renderer +// logic - they are never directly operated on and always passed to the node op +// functions provided via options, so the internal constraint is really just +// a generic object. +export interface RendererNode { + [key: string]: any +} + +export interface RendererElement extends RendererNode {} + // An object exposing the internals of a renderer, passed to tree-shakeable // features so that they can be decoupled from this file. Keys are shortened // to optimize bundle size. -export interface RendererInternals { - p: PatchFn - um: UnmountFn - m: MoveFn - mt: MountComponentFn - mc: MountChildrenFn - pc: PatchChildrenFn - pbc: PatchBlockChildrenFn - n: NextFn +export interface RendererInternals< + HostNode = RendererNode, + HostElement = RendererElement +> { + p: PatchFn + um: UnmountFn + m: MoveFn + mt: MountComponentFn + mc: MountChildrenFn + pc: PatchChildrenFn + pbc: PatchBlockChildrenFn + n: NextFn o: RendererOptions } // These functions are created inside a closure and therefore their types cannot // be directly exported. In order to avoid maintaining function signatures in // two places, we declare them once here and use them inside the closure. -type PatchFn = ( - n1: VNode | null, // null means this is a mount - n2: VNode, - container: HostElement, - anchor?: HostNode | null, +type PatchFn = ( + n1: VNode | null, // null means this is a mount + n2: VNode, + container: RendererElement, + anchor?: RendererNode | null, parentComponent?: ComponentInternalInstance | null, - parentSuspense?: SuspenseBoundary | null, + parentSuspense?: SuspenseBoundary | null, isSVG?: boolean, optimized?: boolean ) => void -type MountChildrenFn = ( - children: VNodeArrayChildren, - container: HostElement, - anchor: HostNode | null, +type MountChildrenFn = ( + children: VNodeArrayChildren, + container: RendererElement, + anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, - parentSuspense: SuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean, optimized: boolean, start?: number ) => void -type PatchChildrenFn = ( - n1: VNode | null, - n2: VNode, - container: HostElement, - anchor: HostNode | null, +type PatchChildrenFn = ( + n1: VNode | null, + n2: VNode, + container: RendererElement, + anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, - parentSuspense: SuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean, optimized?: boolean ) => void -type PatchBlockChildrenFn = ( - oldChildren: VNode[], - newChildren: VNode[], - fallbackContainer: HostElement, +type PatchBlockChildrenFn = ( + oldChildren: VNode[], + newChildren: VNode[], + fallbackContainer: RendererElement, parentComponent: ComponentInternalInstance | null, - parentSuspense: SuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean ) => void -type MoveFn = ( - vnode: VNode, - container: HostElement, - anchor: HostNode | null, +type MoveFn = ( + vnode: VNode, + container: RendererElement, + anchor: RendererNode | null, type: MoveType, - parentSuspense?: SuspenseBoundary | null + parentSuspense?: SuspenseBoundary | null ) => void -type NextFn = ( - vnode: VNode -) => HostNode | null +type NextFn = (vnode: VNode) => RendererNode | null -type UnmountFn = ( - vnode: VNode, +type UnmountFn = ( + vnode: VNode, parentComponent: ComponentInternalInstance | null, - parentSuspense: SuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, doRemove?: boolean ) => void -type UnmountChildrenFn = ( - children: VNode[], +type UnmountChildrenFn = ( + children: VNode[], parentComponent: ComponentInternalInstance | null, - parentSuspense: SuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, doRemove?: boolean, start?: number ) => void -export type MountComponentFn = ( - initialVNode: VNode, - container: HostElement, - anchor: HostNode | null, +export type MountComponentFn = ( + initialVNode: VNode, + container: RendererElement, + anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, - parentSuspense: SuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean ) => void -type ProcessTextOrCommentFn = ( - n1: VNode | null, - n2: VNode, - container: HostElement, - anchor: HostNode | null +type ProcessTextOrCommentFn = ( + n1: VNode | null, + n2: VNode, + container: RendererElement, + anchor: RendererNode | null ) => void -export type SetupRenderEffectFn = ( +export type SetupRenderEffectFn = ( instance: ComponentInternalInstance, - initialVNode: VNode, - container: HostElement, - anchor: HostNode | null, - parentSuspense: SuspenseBoundary | null, + initialVNode: VNode, + container: RendererElement, + anchor: RendererNode | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean ) => void @@ -275,8 +289,8 @@ export const queuePostRenderEffect = __FEATURE_SUSPENSE__ * ``` */ export function createRenderer< - HostNode extends object = any, - HostElement extends HostNode = any + HostNode = RendererNode, + HostElement = RendererElement >(options: RendererOptions) { return baseCreateRenderer(options) } @@ -287,38 +301,26 @@ export function createRenderer< export function createHydrationRenderer( options: RendererOptions ) { - return baseCreateRenderer(options, createHydrationFunctions) + return baseCreateRenderer(options, createHydrationFunctions) } // overload 1: no hydration function baseCreateRenderer< - HostNode extends object = any, - HostElement extends HostNode = any ->( - options: RendererOptions -): Renderer + HostNode = RendererNode, + HostElement = RendererElement +>(options: RendererOptions): Renderer // overload 2: with hydration -function baseCreateRenderer< - HostNode extends object = any, - HostElement extends HostNode = any ->( - options: RendererOptions, +function baseCreateRenderer( + options: RendererOptions, createHydrationFns: typeof createHydrationFunctions ): HydrationRenderer // implementation -function baseCreateRenderer< - HostNode extends object = any, - HostElement extends HostNode = any ->( - options: RendererOptions, +function baseCreateRenderer( + options: RendererOptions, createHydrationFns?: typeof createHydrationFunctions -) { - type HostVNode = VNode - type HostVNodeChildren = VNodeArrayChildren - type HostSuspenseBoundary = SuspenseBoundary - +): any { const { insert: hostInsert, remove: hostRemove, @@ -337,7 +339,7 @@ function baseCreateRenderer< // Note: functions inside this closure should use `const xxx = () => {}` // style in order to prevent being inlined by minifiers. - const patch: PatchFn = ( + const patch: PatchFn = ( n1, n2, container, @@ -427,17 +429,12 @@ function baseCreateRenderer< internals ) } else if (__DEV__) { - warn('Invalid HostVNode type:', type, `(${typeof type})`) + warn('Invalid VNode type:', type, `(${typeof type})`) } } } - const processText: ProcessTextOrCommentFn = ( - n1, - n2, - container, - anchor - ) => { + const processText: ProcessTextOrCommentFn = (n1, n2, container, anchor) => { if (n1 == null) { hostInsert( (n2.el = hostCreateText(n2.children as string)), @@ -445,14 +442,14 @@ function baseCreateRenderer< anchor ) } else { - const el = (n2.el = n1.el) as HostNode + const el = (n2.el = n1.el!) if (n2.children !== n1.children) { hostSetText(el, n2.children as string) } } } - const processCommentNode: ProcessTextOrCommentFn = ( + const processCommentNode: ProcessTextOrCommentFn = ( n1, n2, container, @@ -471,9 +468,9 @@ function baseCreateRenderer< } const mountStaticNode = ( - n2: HostVNode, - container: HostElement, - anchor: HostNode | null, + n2: VNode, + container: RendererElement, + anchor: RendererNode | null, isSVG: boolean ) => { if (n2.el && hostCloneNode !== undefined) { @@ -491,12 +488,12 @@ function baseCreateRenderer< } const processElement = ( - n1: HostVNode | null, - n2: HostVNode, - container: HostElement, - anchor: HostNode | null, + n1: VNode | null, + n2: VNode, + container: RendererElement, + anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, - parentSuspense: HostSuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean, optimized: boolean ) => { @@ -520,15 +517,15 @@ function baseCreateRenderer< } const mountElement = ( - vnode: HostVNode, - container: HostElement, - anchor: HostNode | null, + vnode: VNode, + container: RendererElement, + anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, - parentSuspense: HostSuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean, optimized: boolean ) => { - let el: HostElement + let el: RendererElement let vnodeHook: VNodeHook | undefined | null const { type, @@ -547,7 +544,7 @@ function baseCreateRenderer< // If a vnode has non-null el, it means it's being reused. // Only static vnodes can be reused, so its mounted DOM nodes should be // exactly the same, and we can simply do a clone here. - el = vnode.el = hostCloneNode(vnode.el) as HostElement + el = vnode.el = hostCloneNode(vnode.el) } else { el = vnode.el = hostCreateElement(vnode.type as string, isSVG) // props @@ -583,7 +580,7 @@ function baseCreateRenderer< hostSetElementText(el, vnode.children as string) } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { mountChildren( - vnode.children as HostVNodeChildren, + vnode.children as VNodeArrayChildren, el, null, parentComponent, @@ -611,7 +608,7 @@ function baseCreateRenderer< } } - const mountChildren: MountChildrenFn = ( + const mountChildren: MountChildrenFn = ( children, container, anchor, @@ -623,7 +620,7 @@ function baseCreateRenderer< ) => { for (let i = start; i < children.length; i++) { const child = (children[i] = optimized - ? cloneIfMounted(children[i] as HostVNode) + ? cloneIfMounted(children[i] as VNode) : normalizeVNode(children[i])) patch( null, @@ -639,14 +636,14 @@ function baseCreateRenderer< } const patchElement = ( - n1: HostVNode, - n2: HostVNode, + n1: VNode, + n2: VNode, parentComponent: ComponentInternalInstance | null, - parentSuspense: HostSuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean, optimized: boolean ) => { - const el = (n2.el = n1.el) as HostElement + const el = (n2.el = n1.el!) let { patchFlag, dynamicChildren, dirs } = n2 const oldProps = (n1 && n1.props) || EMPTY_OBJ const newProps = n2.props || EMPTY_OBJ @@ -717,7 +714,7 @@ function baseCreateRenderer< prev, next, isSVG, - n1.children as HostVNode[], + n1.children as VNode[], parentComponent, parentSuspense, unmountChildren @@ -779,7 +776,7 @@ function baseCreateRenderer< } // The fast path for blocks. - const patchBlockChildren: PatchBlockChildrenFn = ( + const patchBlockChildren: PatchBlockChildrenFn = ( oldChildren, newChildren, fallbackContainer, @@ -818,12 +815,12 @@ function baseCreateRenderer< } const patchProps = ( - el: HostElement, - vnode: HostVNode, + el: RendererElement, + vnode: VNode, oldProps: Data, newProps: Data, parentComponent: ComponentInternalInstance | null, - parentSuspense: HostSuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean ) => { if (oldProps !== newProps) { @@ -838,7 +835,7 @@ function baseCreateRenderer< prev, next, isSVG, - vnode.children as HostVNode[], + vnode.children as VNode[], parentComponent, parentSuspense, unmountChildren @@ -854,7 +851,7 @@ function baseCreateRenderer< oldProps[key], null, isSVG, - vnode.children as HostVNode[], + vnode.children as VNode[], parentComponent, parentSuspense, unmountChildren @@ -866,12 +863,12 @@ function baseCreateRenderer< } const processFragment = ( - n1: HostVNode | null, - n2: HostVNode, - container: HostElement, - anchor: HostNode | null, + n1: VNode | null, + n2: VNode, + container: RendererElement, + anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, - parentSuspense: HostSuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean, optimized: boolean ) => { @@ -897,7 +894,7 @@ function baseCreateRenderer< // since they are either generated by the compiler, or implicitly created // from arrays. mountChildren( - n2.children as HostVNodeChildren, + n2.children as VNodeArrayChildren, container, fragmentEndAnchor, parentComponent, @@ -937,12 +934,12 @@ function baseCreateRenderer< } const processComponent = ( - n1: HostVNode | null, - n2: HostVNode, - container: HostElement, - anchor: HostNode | null, + n1: VNode | null, + n2: VNode, + container: RendererElement, + anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, - parentSuspense: HostSuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean, optimized: boolean ) => { @@ -1010,7 +1007,7 @@ function baseCreateRenderer< } } - const mountComponent: MountComponentFn = ( + const mountComponent: MountComponentFn = ( initialVNode, container, anchor, @@ -1073,7 +1070,7 @@ function baseCreateRenderer< } } - const setupRenderEffect: SetupRenderEffectFn = ( + const setupRenderEffect: SetupRenderEffectFn = ( instance, initialVNode, container, @@ -1137,7 +1134,7 @@ function baseCreateRenderer< } else { // updateComponent // This is triggered by mutation of component's own state (next: null) - // OR parent calling processComponent (next: HostVNode) + // OR parent calling processComponent (next: VNode) let { next, bu, u, parent, vnode } = instance let vnodeHook: VNodeHook | null | undefined if (__DEV__) { @@ -1170,7 +1167,7 @@ function baseCreateRenderer< prevTree, nextTree, // parent may have changed if it's in a portal - hostParentNode(prevTree.el as HostNode) as HostElement, + hostParentNode(prevTree.el!)!, // anchor may have changed if it's in a fragment getNextHostNode(prevTree), instance, @@ -1203,7 +1200,7 @@ function baseCreateRenderer< const updateComponentPreRender = ( instance: ComponentInternalInstance, - nextVNode: HostVNode + nextVNode: VNode ) => { nextVNode.component = instance instance.vnode = nextVNode @@ -1212,7 +1209,7 @@ function baseCreateRenderer< resolveSlots(instance, nextVNode.children) } - const patchChildren: PatchChildrenFn = ( + const patchChildren: PatchChildrenFn = ( n1, n2, container, @@ -1236,8 +1233,8 @@ function baseCreateRenderer< // this could be either fully-keyed or mixed (some keyed some not) // presence of patchFlag means children are guaranteed to be arrays patchKeyedChildren( - c1 as HostVNode[], - c2 as HostVNodeChildren, + c1 as VNode[], + c2 as VNodeArrayChildren, container, anchor, parentComponent, @@ -1249,8 +1246,8 @@ function baseCreateRenderer< } else if (patchFlag & PatchFlags.UNKEYED_FRAGMENT) { // unkeyed patchUnkeyedChildren( - c1 as HostVNode[], - c2 as HostVNodeChildren, + c1 as VNode[], + c2 as VNodeArrayChildren, container, anchor, parentComponent, @@ -1266,7 +1263,7 @@ function baseCreateRenderer< if (shapeFlag & ShapeFlags.TEXT_CHILDREN) { // text children fast path if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) { - unmountChildren(c1 as HostVNode[], parentComponent, parentSuspense) + unmountChildren(c1 as VNode[], parentComponent, parentSuspense) } if (c2 !== c1) { hostSetElementText(container, c2 as string) @@ -1277,8 +1274,8 @@ function baseCreateRenderer< if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { // two arrays, cannot assume anything, do full diff patchKeyedChildren( - c1 as HostVNode[], - c2 as HostVNodeChildren, + c1 as VNode[], + c2 as VNodeArrayChildren, container, anchor, parentComponent, @@ -1288,12 +1285,7 @@ function baseCreateRenderer< ) } else { // no new children, just unmount old - unmountChildren( - c1 as HostVNode[], - parentComponent, - parentSuspense, - true - ) + unmountChildren(c1 as VNode[], parentComponent, parentSuspense, true) } } else { // prev children was text OR null @@ -1304,7 +1296,7 @@ function baseCreateRenderer< // mount new if array if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { mountChildren( - c2 as HostVNodeChildren, + c2 as VNodeArrayChildren, container, anchor, parentComponent, @@ -1318,12 +1310,12 @@ function baseCreateRenderer< } const patchUnkeyedChildren = ( - c1: HostVNode[], - c2: HostVNodeChildren, - container: HostElement, - anchor: HostNode | null, + c1: VNode[], + c2: VNodeArrayChildren, + container: RendererElement, + anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, - parentSuspense: HostSuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean, optimized: boolean ) => { @@ -1335,7 +1327,7 @@ function baseCreateRenderer< let i for (i = 0; i < commonLength; i++) { const nextChild = (c2[i] = optimized - ? cloneIfMounted(c2[i] as HostVNode) + ? cloneIfMounted(c2[i] as VNode) : normalizeVNode(c2[i])) patch( c1[i], @@ -1368,12 +1360,12 @@ function baseCreateRenderer< // can be all-keyed or mixed const patchKeyedChildren = ( - c1: HostVNode[], - c2: HostVNodeChildren, - container: HostElement, - parentAnchor: HostNode | null, + c1: VNode[], + c2: VNodeArrayChildren, + container: RendererElement, + parentAnchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, - parentSuspense: HostSuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, isSVG: boolean, optimized: boolean ) => { @@ -1388,7 +1380,7 @@ function baseCreateRenderer< while (i <= e1 && i <= e2) { const n1 = c1[i] const n2 = (c2[i] = optimized - ? cloneIfMounted(c2[i] as HostVNode) + ? cloneIfMounted(c2[i] as VNode) : normalizeVNode(c2[i])) if (isSameVNodeType(n1, n2)) { patch( @@ -1413,7 +1405,7 @@ function baseCreateRenderer< while (i <= e1 && i <= e2) { const n1 = c1[e1] const n2 = (c2[e2] = optimized - ? cloneIfMounted(c2[e2] as HostVNode) + ? cloneIfMounted(c2[e2] as VNode) : normalizeVNode(c2[e2])) if (isSameVNodeType(n1, n2)) { patch( @@ -1443,13 +1435,12 @@ function baseCreateRenderer< if (i > e1) { if (i <= e2) { const nextPos = e2 + 1 - const anchor = - nextPos < l2 ? (c2[nextPos] as HostVNode).el : parentAnchor + const anchor = nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchor while (i <= e2) { patch( null, (c2[i] = optimized - ? cloneIfMounted(c2[i] as HostVNode) + ? cloneIfMounted(c2[i] as VNode) : normalizeVNode(c2[i])), container, anchor, @@ -1488,7 +1479,7 @@ function baseCreateRenderer< const keyToNewIndexMap: Map = new Map() for (i = s2; i <= e2; i++) { const nextChild = (c2[i] = optimized - ? cloneIfMounted(c2[i] as HostVNode) + ? cloneIfMounted(c2[i] as VNode) : normalizeVNode(c2[i])) if (nextChild.key != null) { if (__DEV__ && keyToNewIndexMap.has(nextChild.key)) { @@ -1533,7 +1524,7 @@ function baseCreateRenderer< for (j = s2; j <= e2; j++) { if ( newIndexToOldIndexMap[j - s2] === 0 && - isSameVNodeType(prevChild, c2[j] as HostVNode) + isSameVNodeType(prevChild, c2[j] as VNode) ) { newIndex = j break @@ -1551,7 +1542,7 @@ function baseCreateRenderer< } patch( prevChild, - c2[newIndex] as HostVNode, + c2[newIndex] as VNode, container, null, parentComponent, @@ -1572,11 +1563,9 @@ function baseCreateRenderer< // looping backwards so that we can use last patched node as anchor for (i = toBePatched - 1; i >= 0; i--) { const nextIndex = s2 + i - const nextChild = c2[nextIndex] as HostVNode + const nextChild = c2[nextIndex] as VNode const anchor = - nextIndex + 1 < l2 - ? (c2[nextIndex + 1] as HostVNode).el - : parentAnchor + nextIndex + 1 < l2 ? (c2[nextIndex + 1] as VNode).el : parentAnchor if (newIndexToOldIndexMap[i] === 0) { // mount new patch( @@ -1602,7 +1591,7 @@ function baseCreateRenderer< } } - const move: MoveFn = ( + const move: MoveFn = ( vnode, container, anchor, @@ -1619,7 +1608,7 @@ function baseCreateRenderer< } if (vnode.type === Fragment) { hostInsert(vnode.el!, container, anchor) - const children = vnode.children as HostVNode[] + const children = vnode.children as VNode[] for (let i = 0; i < children.length; i++) { move(children[i], container, anchor, type) } @@ -1657,7 +1646,7 @@ function baseCreateRenderer< } } - const unmount: UnmountFn = ( + const unmount: UnmountFn = ( vnode, parentComponent, parentSuspense, @@ -1696,11 +1685,7 @@ function baseCreateRenderer< // fast path for block nodes: only need to unmount dynamic children. unmountChildren(dynamicChildren, parentComponent, parentSuspense) } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { - unmountChildren( - children as HostVNode[], - parentComponent, - parentSuspense - ) + unmountChildren(children as VNode[], parentComponent, parentSuspense) } if (doRemove) { @@ -1717,7 +1702,7 @@ function baseCreateRenderer< } } - const remove = (vnode: HostVNode) => { + const remove = (vnode: VNode) => { const { type, el, anchor, transition } = vnode if (type === Fragment) { removeFragment(el!, anchor!) @@ -1748,7 +1733,7 @@ function baseCreateRenderer< } } - const removeFragment = (cur: HostNode, end: HostNode) => { + const removeFragment = (cur: RendererNode, end: RendererNode) => { // For fragments, directly remove all contained DOM nodes. // (fragment child nodes cannot have transition) let next @@ -1762,7 +1747,7 @@ function baseCreateRenderer< const unmountComponent = ( instance: ComponentInternalInstance, - parentSuspense: HostSuspenseBoundary | null, + parentSuspense: SuspenseBoundary | null, doRemove?: boolean ) => { if (__HMR__ && instance.type.__hmrId) { @@ -1819,7 +1804,7 @@ function baseCreateRenderer< } } - const unmountChildren: UnmountChildrenFn = ( + const unmountChildren: UnmountChildrenFn = ( children, parentComponent, parentSuspense, @@ -1831,7 +1816,7 @@ function baseCreateRenderer< } } - const getNextHostNode: NextFn = vnode => { + const getNextHostNode: NextFn = vnode => { if (vnode.shapeFlag & ShapeFlags.COMPONENT) { return getNextHostNode(vnode.component!.subTree) } @@ -1845,7 +1830,7 @@ function baseCreateRenderer< rawRef: VNodeNormalizedRef, oldRawRef: VNodeNormalizedRef | null, parent: ComponentInternalInstance, - value: HostNode | ComponentPublicInstance | null + value: RendererNode | ComponentPublicInstance | null ) => { const [owner, ref] = rawRef if (__DEV__ && !owner) { @@ -1887,12 +1872,7 @@ function baseCreateRenderer< } } - type HostRootElement = HostElement & { _vnode: HostVNode | null } - - const render: RootRenderFunction = ( - vnode, - container: HostRootElement - ) => { + const render: RootRenderFunction = (vnode, container) => { if (vnode == null) { if (container._vnode) { unmount(container._vnode, null, null, true) @@ -1904,7 +1884,7 @@ function baseCreateRenderer< container._vnode = vnode } - const internals: RendererInternals = { + const internals: RendererInternals = { p: patch, um: unmount, m: move, @@ -1928,7 +1908,7 @@ function baseCreateRenderer< return { render, hydrate, - createApp: createAppAPI(render, hydrate) as CreateAppFunction + createApp: createAppAPI(render, hydrate) } } diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index 151cc866..29b1fb0d 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -31,6 +31,7 @@ import { warn } from './warning' import { currentScopeId } from './helpers/scopeId' import { PortalImpl, isPortal } from './components/Portal' import { currentRenderingInstance } from './componentRenderUtils' +import { RendererNode, RendererElement } from './renderer' export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as { __isFragment: true @@ -81,40 +82,31 @@ export interface VNodeProps { onVnodeUnmounted?: VNodeMountHook | VNodeMountHook[] } -type VNodeChildAtom = - | VNode +type VNodeChildAtom = VNode | string | number | boolean | null | void + +export interface VNodeArrayChildren< + HostNode = RendererNode, + HostElement = RendererElement +> extends Array {} + +export type VNodeChild = VNodeChildAtom | VNodeArrayChildren + +export type VNodeNormalizedChildren = | string - | number - | boolean - | null - | void - -export interface VNodeArrayChildren - extends Array< - | VNodeArrayChildren - | VNodeChildAtom - > {} - -export type VNodeChild = - | VNodeChildAtom - | VNodeArrayChildren - -export type VNodeNormalizedChildren = - | string - | VNodeArrayChildren + | VNodeArrayChildren | RawSlots | null -export interface VNode { +export interface VNode { _isVNode: true type: VNodeTypes props: VNodeProps | null key: string | number | null ref: VNodeNormalizedRef | null scopeId: string | null // SFC only - children: VNodeNormalizedChildren + children: VNodeNormalizedChildren component: ComponentInternalInstance | null - suspense: SuspenseBoundary | null + suspense: SuspenseBoundary | null dirs: DirectiveBinding[] | null transition: TransitionHooks | null @@ -376,7 +368,7 @@ export function createCommentVNode( : createVNode(Comment, null, text) } -export function normalizeVNode(child: VNodeChild): VNode { +export function normalizeVNode(child: VNodeChild): VNode { if (child == null || typeof child === 'boolean') { // empty placeholder return createVNode(Comment) diff --git a/packages/runtime-dom/src/components/Transition.ts b/packages/runtime-dom/src/components/Transition.ts index 1a4e2c82..f82b4e9c 100644 --- a/packages/runtime-dom/src/components/Transition.ts +++ b/packages/runtime-dom/src/components/Transition.ts @@ -13,7 +13,7 @@ import { ErrorCodes } from 'packages/runtime-core/src/errorHandling' const TRANSITION = 'transition' const ANIMATION = 'animation' -export interface TransitionProps extends BaseTransitionProps { +export interface TransitionProps extends BaseTransitionProps { name?: string type?: typeof TRANSITION | typeof ANIMATION css?: boolean @@ -76,7 +76,7 @@ export function resolveTransitionProps({ leaveActiveClass = `${name}-leave-active`, leaveToClass = `${name}-leave-to`, ...baseProps -}: TransitionProps): BaseTransitionProps { +}: TransitionProps): BaseTransitionProps { if (!css) { return baseProps } @@ -94,7 +94,7 @@ export function resolveTransitionProps({ enterToClass = appearToClass } - type Hook = (el: HTMLElement, done?: () => void) => void + type Hook = (el: Element, done?: () => void) => void const finishEnter: Hook = (el, done) => { removeTransitionClass(el, enterToClass) @@ -124,7 +124,7 @@ export function resolveTransitionProps({ onEnter(el, done) { nextFrame(() => { const resolve = () => finishEnter(el, done) - onEnter && callHookWithErrorHandling(onEnter, [el, resolve]) + onEnter && callHookWithErrorHandling(onEnter as Hook, [el, resolve]) removeTransitionClass(el, enterFromClass) addTransitionClass(el, enterToClass) if (!(onEnter && onEnter.length > 1)) { @@ -141,7 +141,7 @@ export function resolveTransitionProps({ addTransitionClass(el, leaveFromClass) nextFrame(() => { const resolve = () => finishLeave(el, done) - onLeave && callHookWithErrorHandling(onLeave, [el, resolve]) + onLeave && callHookWithErrorHandling(onLeave as Hook, [el, resolve]) removeTransitionClass(el, leaveFromClass) addTransitionClass(el, leaveToClass) if (!(onLeave && onLeave.length > 1)) { @@ -199,17 +199,21 @@ export interface ElementWithTransition extends HTMLElement { _vtc?: Set } -export function addTransitionClass(el: ElementWithTransition, cls: string) { +export function addTransitionClass(el: Element, cls: string) { cls.split(/\s+/).forEach(c => c && el.classList.add(c)) - ;(el._vtc || (el._vtc = new Set())).add(cls) + ;( + (el as ElementWithTransition)._vtc || + ((el as ElementWithTransition)._vtc = new Set()) + ).add(cls) } -export function removeTransitionClass(el: ElementWithTransition, cls: string) { +export function removeTransitionClass(el: Element, cls: string) { cls.split(/\s+/).forEach(c => c && el.classList.remove(c)) - if (el._vtc) { - el._vtc.delete(cls) - if (!el._vtc!.size) { - el._vtc = undefined + const { _vtc } = el as ElementWithTransition + if (_vtc) { + _vtc.delete(cls) + if (!_vtc!.size) { + ;(el as ElementWithTransition)._vtc = undefined } } } diff --git a/packages/runtime-dom/src/components/TransitionGroup.ts b/packages/runtime-dom/src/components/TransitionGroup.ts index 490edb8c..0cefb8f0 100644 --- a/packages/runtime-dom/src/components/TransitionGroup.ts +++ b/packages/runtime-dom/src/components/TransitionGroup.ts @@ -52,8 +52,8 @@ const TransitionGroupImpl = { hasMove = hasMove === null ? (hasMove = hasCSSTransform( - prevChildren[0].el, - instance.vnode.el, + prevChildren[0].el as ElementWithTransition, + instance.vnode.el as Node, moveClass )) : hasMove @@ -71,17 +71,17 @@ const TransitionGroupImpl = { forceReflow() movedChildren.forEach(c => { - const el = c.el + const el = c.el as ElementWithTransition const style = el.style addTransitionClass(el, moveClass) - style.transform = style.WebkitTransform = style.transitionDuration = '' - const cb = (el._moveCb = (e: TransitionEvent) => { + style.transform = style.webkitTransform = style.transitionDuration = '' + const cb = ((el as any)._moveCb = (e: TransitionEvent) => { if (e && e.target !== el) { return } if (!e || /transform$/.test(e.propertyName)) { el.removeEventListener('transitionend', cb) - el._moveCb = null + ;(el as any)._moveCb = null removeTransitionClass(el, moveClass) } }) @@ -120,7 +120,7 @@ const TransitionGroupImpl = { child, resolveTransitionHooks(child, cssTransitionProps, state, instance) ) - positionMap.set(child, child.el.getBoundingClientRect()) + positionMap.set(child, (child.el as Element).getBoundingClientRect()) } } @@ -145,16 +145,17 @@ if (__DEV__) { } function callPendingCbs(c: VNode) { - if (c.el._moveCb) { - c.el._moveCb() + const el = c.el as any + if (el._moveCb) { + el._moveCb() } - if (c.el._enterCb) { - c.el._enterCb() + if (el._enterCb) { + el._enterCb() } } function recordPosition(c: VNode) { - newPositionMap.set(c, c.el.getBoundingClientRect()) + newPositionMap.set(c, (c.el as Element).getBoundingClientRect()) } function applyTranslation(c: VNode): VNode | undefined { @@ -163,8 +164,8 @@ function applyTranslation(c: VNode): VNode | undefined { const dx = oldPos.left - newPos.left const dy = oldPos.top - newPos.top if (dx || dy) { - const s = c.el.style - s.transform = s.WebkitTransform = `translate(${dx}px,${dy}px)` + const s = (c.el as HTMLElement).style + s.transform = s.webkitTransform = `translate(${dx}px,${dy}px)` s.transitionDuration = '0s' return c } diff --git a/packages/runtime-dom/src/directives/vModel.ts b/packages/runtime-dom/src/directives/vModel.ts index 703ee722..9cc4a5d2 100644 --- a/packages/runtime-dom/src/directives/vModel.ts +++ b/packages/runtime-dom/src/directives/vModel.ts @@ -11,11 +11,11 @@ import { isArray, looseEqual, looseIndexOf } from '@vue/shared' const getModelAssigner = (vnode: VNode): ((value: any) => void) => vnode.props!['onUpdate:modelValue'] -function onCompositionStart(e: CompositionEvent) { +function onCompositionStart(e: Event) { ;(e.target as any).composing = true } -function onCompositionEnd(e: CompositionEvent) { +function onCompositionEnd(e: Event) { const target = e.target as any if (target.composing) { target.composing = false diff --git a/packages/runtime-dom/src/directives/vOn.ts b/packages/runtime-dom/src/directives/vOn.ts index f2f11622..f701394c 100644 --- a/packages/runtime-dom/src/directives/vOn.ts +++ b/packages/runtime-dom/src/directives/vOn.ts @@ -6,7 +6,7 @@ type KeyedEvent = KeyboardEvent | MouseEvent | TouchEvent const modifierGuards: Record< string, - (e: Event, modifiers?: string[]) => void | boolean + (e: Event, modifiers: string[]) => void | boolean > = { stop: e => e.stopPropagation(), prevent: e => e.preventDefault(), @@ -18,7 +18,7 @@ const modifierGuards: Record< left: e => 'button' in e && (e as MouseEvent).button !== 0, middle: e => 'button' in e && (e as MouseEvent).button !== 1, right: e => 'button' in e && (e as MouseEvent).button !== 2, - exact: (e, modifiers: string[]) => + exact: (e, modifiers) => systemModifiers.some(m => (e as any)[`${m}Key`] && !modifiers.includes(m)) } diff --git a/packages/runtime-dom/src/index.ts b/packages/runtime-dom/src/index.ts index 148b08ca..26b188f0 100644 --- a/packages/runtime-dom/src/index.ts +++ b/packages/runtime-dom/src/index.ts @@ -40,7 +40,7 @@ function ensureHydrationRenderer() { // use explicit type casts here to avoid import() calls in rolled-up d.ts export const render = ((...args) => { ensureRenderer().render(...args) -}) as RootRenderFunction +}) as RootRenderFunction export const hydrate = ((...args) => { ensureHydrationRenderer().hydrate(...args) diff --git a/packages/runtime-test/src/index.ts b/packages/runtime-test/src/index.ts index 7b6dcfd9..578eb416 100644 --- a/packages/runtime-test/src/index.ts +++ b/packages/runtime-test/src/index.ts @@ -4,7 +4,7 @@ import { RootRenderFunction, CreateAppFunction } from '@vue/runtime-core' -import { nodeOps, TestNode, TestElement } from './nodeOps' +import { nodeOps, TestElement } from './nodeOps' import { patchProp } from './patchProp' import { serializeInner } from './serialize' @@ -13,7 +13,7 @@ const { render: baseRender, createApp: baseCreateApp } = createRenderer({ ...nodeOps }) -export const render = baseRender as RootRenderFunction +export const render = baseRender as RootRenderFunction export const createApp = baseCreateApp as CreateAppFunction // convenience for one-off render validations diff --git a/packages/vue/examples/__tests__/e2eUtils.ts b/packages/vue/examples/__tests__/e2eUtils.ts index db79a9c5..502dd547 100644 --- a/packages/vue/examples/__tests__/e2eUtils.ts +++ b/packages/vue/examples/__tests__/e2eUtils.ts @@ -42,7 +42,7 @@ export function setupPuppeteer() { } async function value(selector: string) { - return await page.$eval(selector, (node: HTMLInputElement) => node.value) + return await page.$eval(selector, node => (node as HTMLInputElement).value) } async function html(selector: string) { @@ -58,14 +58,17 @@ export function setupPuppeteer() { } async function isVisible(selector: string) { - const display = await page.$eval(selector, (node: HTMLElement) => { + const display = await page.$eval(selector, node => { return window.getComputedStyle(node).display }) return display !== 'none' } async function isChecked(selector: string) { - return await page.$eval(selector, (node: HTMLInputElement) => node.checked) + return await page.$eval( + selector, + node => (node as HTMLInputElement).checked + ) } async function isFocused(selector: string) { @@ -74,13 +77,13 @@ export function setupPuppeteer() { async function setValue(selector: string, value: string) { const el = (await page.$(selector))! - await el.evaluate((node: HTMLInputElement) => (node.value = '')) + await el.evaluate(node => ((node as HTMLInputElement).value = '')) await el.type(value) } async function enterValue(selector: string, value: string) { const el = (await page.$(selector))! - await el.evaluate((node: HTMLInputElement) => (node.value = '')) + await el.evaluate(node => ((node as HTMLInputElement).value = '')) await el.type(value) await el.press('Enter') } @@ -88,7 +91,7 @@ export function setupPuppeteer() { async function clearValue(selector: string) { return await page.$eval( selector, - (node: HTMLInputElement) => (node.value = '') + node => ((node as HTMLInputElement).value = '') ) } diff --git a/packages/vue/examples/__tests__/markdown.spec.ts b/packages/vue/examples/__tests__/markdown.spec.ts index cc9f4e87..77409fa7 100644 --- a/packages/vue/examples/__tests__/markdown.spec.ts +++ b/packages/vue/examples/__tests__/markdown.spec.ts @@ -18,7 +18,7 @@ describe('e2e: markdown', () => { await page().type('textarea', '\n## foo\n\n- bar\n- baz') // assert the output is not updated yet because of debounce expect(await html('#editor div')).toBe('

hello

\n') - await page().waitFor(30) + await page().waitFor(100) expect(await html('#editor div')).toBe( '

hello

\n' + '

foo

\n' + diff --git a/tsconfig.json b/tsconfig.json index 4a61ca37..ade912a1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,10 +7,8 @@ "module": "esnext", "moduleResolution": "node", "allowJs": false, + "strict": true, "noUnusedLocals": true, - "strictNullChecks": true, - "noImplicitAny": true, - "noImplicitThis": true, "experimentalDecorators": true, "resolveJsonModule": true, "esModuleInterop": true,