import { ExtractPropTypes, ComponentPropsOptions } from './componentProps'
import { EmitsOptions } from './componentEmits'
import { isFunction } from '@vue/shared'
-import { VNodeProps } from './vnode'
+import {
+ VNodeProps,
+ AllowedComponentProps,
+ ComponentCustomProps
+} from './vnode'
// defineComponent is a utility that is primarily used for type inference
// when declaring components. Type inference is provided in the component
{},
{},
// public props
- VNodeProps & Props
+ VNodeProps & Props & AllowedComponentProps & ComponentCustomProps
>
> &
FunctionalComponent<Props>
Mixin,
Extends,
E,
- VNodeProps & Props
+ VNodeProps & Props & AllowedComponentProps & ComponentCustomProps
>
> &
ComponentOptionsWithoutProps<
M,
Mixin,
Extends,
- E
+ E,
+ AllowedComponentProps & ComponentCustomProps
>
> &
ComponentOptionsWithArrayProps<
Mixin,
Extends,
E,
- VNodeProps
+ VNodeProps & AllowedComponentProps & ComponentCustomProps
>
> &
ComponentOptionsWithObjectProps<
}
export interface SuspenseBoundary {
- vnode: VNode
+ vnode: VNode<RendererNode, RendererElement, SuspenseProps>
parent: SuspenseBoundary | null
parentComponent: ComponentInternalInstance | null
isSVG: boolean
import { isString, ShapeFlags } from '@vue/shared'
import { warn } from '../warning'
+export type TeleportVNode = VNode<RendererNode, RendererElement, TeleportProps>
+
export interface TeleportProps {
to: string | RendererElement
disabled?: boolean
export const TeleportImpl = {
__isTeleport: true,
process(
- n1: VNode | null,
- n2: VNode,
+ n1: TeleportVNode | null,
+ n2: TeleportVNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
insert(placeholder, container, anchor)
insert(mainAnchor, container, anchor)
- const target = (n2.target = resolveTarget(
- n2.props as TeleportProps,
- querySelector
- ))
+ const target = (n2.target = resolveTarget(n2.props, querySelector))
const targetAnchor = (n2.targetAnchor = createText(''))
if (target) {
insert(targetAnchor, target)
// target changed
if ((n2.props && n2.props.to) !== (n1.props && n1.props.to)) {
const nextTarget = (n2.target = resolveTarget(
- n2.props as TeleportProps,
+ n2.props,
querySelector
))
if (nextTarget) {
function hydrateTeleport(
node: Node,
- vnode: VNode,
+ vnode: TeleportVNode,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
optimized: boolean,
) => Node | null
): Node | null {
const target = (vnode.target = resolveTarget<Element>(
- vnode.props as TeleportProps,
+ vnode.props,
querySelector
))
if (target) {
__v_isVNode?: never
// used to differ from Array children
[Symbol.iterator]?: never
-}
+} & { [key: string]: any }
type RawChildren =
| string
SuspenseBoundary,
queueEffectWithSuspense
} from './components/Suspense'
-import { TeleportImpl } from './components/Teleport'
+import { TeleportImpl, TeleportVNode } from './components/Teleport'
export type RootHydrateFunction = (
vnode: VNode<Node, Element>,
} else {
nextNode = (vnode.type as typeof TeleportImpl).hydrate(
node,
- vnode,
+ vnode as TeleportVNode,
parentComponent,
parentSuspense,
optimized,
// Advanced render function utilities
export { createVNode, cloneVNode, mergeProps, isVNode } from './vnode'
// VNode types
-export { Fragment, Text, Comment, Static } from './vnode'
+export { Fragment, Text, Comment, Static, ComponentCustomProps } from './vnode'
// Built-in components
export { Teleport, TeleportProps } from './components/Teleport'
export { Suspense, SuspenseProps } from './components/Suspense'
queueEffectWithSuspense,
SuspenseImpl
} from './components/Suspense'
-import { TeleportImpl } from './components/Teleport'
+import { TeleportImpl, TeleportVNode } from './components/Teleport'
import { isKeepAlive, KeepAliveContext } from './components/KeepAlive'
import { registerHMR, unregisterHMR, isHmrUpdating } from './hmr'
import {
)
} else if (shapeFlag & ShapeFlags.TELEPORT) {
;(type as typeof TeleportImpl).process(
- n1,
- n2,
+ n1 as TeleportVNode,
+ n2 as TeleportVNode,
container,
anchor,
parentComponent,
| VNodeMountHook[]
| VNodeUpdateHook[]
-export interface VNodeProps {
- [key: string]: any
+export interface ComponentCustomProps {}
+export interface AllowedComponentProps {
+ class?: unknown
+ style?: unknown
+}
+
+// https://github.com/microsoft/TypeScript/issues/33099
+export type VNodeProps = {
key?: string | number
ref?: VNodeRef
| RawSlots
| null
-export interface VNode<HostNode = RendererNode, HostElement = RendererElement> {
+export interface VNode<
+ HostNode = RendererNode,
+ HostElement = RendererElement,
+ ExtraProps = { [key: string]: any }
+> {
/**
* @internal
*/
*/
__v_skip: true
type: VNodeTypes
- props: VNodeProps | null
+ props: (VNodeProps & ExtraProps) | null
key: string | number | null
ref: VNodeNormalizedRef | null
scopeId: string | null // SFC only
const incoming = toMerge[key]
if (existing !== incoming) {
ret[key] = existing
- ? [].concat(existing as any, toMerge[key])
+ ? [].concat(existing as any, toMerge[key] as any)
: incoming
}
} else {
+++ /dev/null
-import { defineComponent, expectError, expectType } from './index'
-
-declare module '@vue/runtime-core' {
- interface ComponentCustomOptions {
- test?(n: number): void
- }
-
- interface ComponentCustomProperties {
- state: 'stopped' | 'running'
- }
-}
-
-export const Custom = defineComponent({
- data: () => ({ counter: 0 }),
-
- test(n) {
- expectType<number>(n)
- },
-
- methods: {
- aMethod() {
- // @ts-expect-error
- expectError(this.notExisting)
- this.counter++
- this.state = 'running'
- // @ts-expect-error
- expectError((this.state = 'not valid'))
- }
- }
-})
--- /dev/null
+import { defineComponent, expectError, expectType } from './index'
+
+declare module '@vue/runtime-core' {
+ interface ComponentCustomOptions {
+ test?(n: number): void
+ }
+
+ interface ComponentCustomProperties {
+ state: 'stopped' | 'running'
+ }
+
+ interface ComponentCustomProps {
+ custom?: number
+ }
+}
+
+export const Custom = defineComponent({
+ props: {
+ bar: String,
+ baz: {
+ type: Number,
+ required: true
+ }
+ },
+
+ data: () => ({ counter: 0 }),
+
+ test(n) {
+ expectType<number>(n)
+ },
+
+ methods: {
+ aMethod() {
+ // @ts-expect-error
+ expectError(this.notExisting)
+ this.counter++
+ this.state = 'running'
+ // @ts-expect-error
+ expectError((this.state = 'not valid'))
+ }
+ }
+})
+
+expectType<JSX.Element>(<Custom baz={1} />)
+expectType<JSX.Element>(<Custom custom={1} baz={1} />)
+expectType<JSX.Element>(<Custom bar="bar" baz={1} />)
+
+// @ts-expect-error
+expectType<JSX.Element>(<Custom />)
+// @ts-expect-error
+expectError(<Custom bar="bar" />)
+// @ts-expect-error
+expectError(<Custom baz="baz" />)
+// @ts-expect-error
+expectError(<Custom baz={1} notExist={1} />)
+// @ts-expect-error
+expectError(<Custom baz={1} custom="custom" />)
eee={() => ({ a: 'eee' })}
fff={(a, b) => ({ a: a > +b })}
hhh={false}
- // should allow extraneous as attrs
+ // should allow class/style as attrs
class="bar"
+ style={{ color: 'red' }}
// should allow key
key={'foo'}
// should allow ref