Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
pikax committed Oct 6, 2023
1 parent 23e78ae commit 00d7d87
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 13 deletions.
68 changes: 68 additions & 0 deletions packages/dts-test/componentRef.test-d.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {
Ref,
defineComponent,
ComponentPublicInstance,
FunctionalComponent
} from 'vue'
import { expectType } from './utils'

// html component ref
declare let videoEl: HTMLVideoElement | null
declare let videoElRef: Ref<HTMLVideoElement | null>
;<video ref={e => (videoEl = e)} />
;<video ref={videoElRef} />

// @ts-expect-error
expectType(<a ref={e => (videoEl = e)} />)
// @ts-expect-error
expectType(<a ref={videoEl} />)

const Comp = defineComponent({
props: {
test: String
}
})

// Component based ref
declare let myComp: InstanceType<typeof Comp> | null
declare let myCompRef: Ref<InstanceType<typeof Comp> | null>
declare let anyComp: ComponentPublicInstance | null
expectType<JSX.Element>(<Comp ref={e => (myComp = e)} />)
expectType<JSX.Element>(<Comp ref={myCompRef} />)
expectType<JSX.Element>(<Comp ref={e => (anyComp = e)} />)

declare let wrongComponent: ComponentPublicInstance<{ a: string }>

// @ts-expect-error wrong Component type
expectType<JSX.Element>(<Comp ref={e => (wrongComponent = e)} />)

// Function
declare function FuncComp(props: { foo: string }): any

expectType<JSX.Element>(<FuncComp foo="test" ref={e => e?.$props.foo} />)

// @ts-expect-error not valid prop
expectType<JSX.Element>(<FuncComp foo="test" ref={e => e?.$props.bar} />)

declare const FuncEmitComp: FunctionalComponent<{ foo: string }, ['test']>

expectType<JSX.Element>(
<FuncEmitComp foo="test" ref={e => e?.$props.foo && e?.$emit('test')} />
)

// @ts-expect-error not valid prop
expectType<JSX.Element>(<FuncEmitComp foo="test" ref={e => e?.$props.bar} />)

// @ts-expect-error not valid emit
expectType<JSX.Element>(<FuncEmitComp foo="test" ref={e => e?.$emit('bar')} />)

// Class
declare const CustomComp: {
new (): { $props: { foo: string } }
}

declare let customComp: InstanceType<typeof CustomComp> | null
expectType<JSX.Element>(<CustomComp foo="test" ref={e => (customComp = e)} />)

// @ts-expect-error not valid prop
expectType<JSX.Element>(<CustomComp foo="test" ref={e => e?.$props.bar} />)
14 changes: 5 additions & 9 deletions packages/runtime-core/src/vnode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ import { convertLegacyComponent } from './compat/component'
import { convertLegacyVModelProps } from './compat/componentVModel'
import { defineLegacyVNodeProperties } from './compat/renderFn'
import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
import { ComponentPublicInstance } from './componentPublicInstance'

export const Fragment = Symbol.for('v-fgt') as any as {
__isFragment: true
Expand All @@ -69,13 +68,10 @@ export type VNodeTypes =
| typeof Suspense
| typeof SuspenseImpl

export type VNodeRef =
export type VNodeRef<T = object> =
| string
| Ref
| ((
ref: Element | ComponentPublicInstance | null,
refs: Record<string, any>
) => void)
| Ref<T | null>
| ((ref: T | null, refs: Record<string, any>) => void)

export type VNodeNormalizedRefAtom = {
i: ComponentInternalInstance
Expand All @@ -97,9 +93,9 @@ export type VNodeHook =
| VNodeUpdateHook[]

// https://github.com/microsoft/TypeScript/issues/33099
export type VNodeProps = {
export type VNodeProps<ComponentInstance = object> = {
key?: string | number | symbol
ref?: VNodeRef
ref?: VNodeRef<ComponentInstance>
ref_for?: boolean
ref_key?: string

Expand Down
10 changes: 8 additions & 2 deletions packages/runtime-dom/src/jsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1351,12 +1351,18 @@ import { VNodeRef } from '@vue/runtime-core'

export type ReservedProps = {
key?: string | number | symbol
ref?: VNodeRef
// ref?: VNodeRef<T>
ref_for?: boolean
ref_key?: string
}

export type NativeElements = {
[K in keyof IntrinsicElementAttributes]: IntrinsicElementAttributes[K] &
ReservedProps
ReservedProps & {
ref?: VNodeRef<
K extends keyof HTMLElementTagNameMap
? HTMLElementTagNameMap[K]
: HTMLElement
>
}
}
31 changes: 30 additions & 1 deletion packages/vue/jsx-runtime/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { VNode, ReservedProps, NativeElements } from '@vue/runtime-dom'
import type {
VNode,
ReservedProps,
NativeElements,
VNodeRef
} from '@vue/runtime-dom'

/**
* JSX namespace for usage with @jsxImportsSource directive
Expand All @@ -21,4 +26,28 @@ export namespace JSX {
[name: string]: any
}
export interface IntrinsicAttributes extends ReservedProps {}

export interface IntrinsicClassAttributes<T> extends ReservedProps {}

// managed based props, this will be used to override the props defined
// used mainly to set `ref` property correct
export type LibraryManagedAttributes<C, P> = {
ref?: P extends { ref?: infer R } // it already exists, most likely NativeElement
? R
: VNodeRef<
C extends { new (): infer I }
? I
: C extends FunctionalComponent
? C
: C extends FunctionalComponent<infer Props, infer E>
? E extends EmitsOptions
? ComponentPublicInstance<Props, {}, {}, {}, {}, E>
: ComponentPublicInstance<Props>
: C extends (props: infer Props) => any
? ComponentPublicInstance<Props>
: C extends () => any
? ComponentPublicInstance<{}>
: Element | ComponentPublicInstance
>
} & P
}
34 changes: 33 additions & 1 deletion packages/vue/jsx.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
// global JSX namespace registration
// somehow we have to copy=pase the jsx-runtime types here to make TypeScript happy
import type { VNode, ReservedProps, NativeElements } from '@vue/runtime-dom'
import type {
VNode,
ReservedProps,
NativeElements,
FunctionalComponent,
EmitsOptions,
ComponentPublicInstance,
VNodeRef
} from '@vue/runtime-dom'

declare global {
namespace JSX {
Expand All @@ -17,5 +25,29 @@ declare global {
[name: string]: any
}
export interface IntrinsicAttributes extends ReservedProps {}

export interface IntrinsicClassAttributes<T> extends ReservedProps {}

// managed based props, this will be used to override the props defined
// used mainly to set `ref` property correct
export type LibraryManagedAttributes<C, P> = {
ref?: P extends { ref?: infer R } // it already exists, most likely NativeElement
? R
: VNodeRef<
C extends { new (): infer I }
? I
: C extends FunctionalComponent
? C
: C extends FunctionalComponent<infer Props, infer E>
? E extends EmitsOptions
? ComponentPublicInstance<Props, {}, {}, {}, {}, E>
: ComponentPublicInstance<Props>
: C extends (props: infer Props) => any
? ComponentPublicInstance<Props>
: C extends () => any
? ComponentPublicInstance<{}>
: Element | ComponentPublicInstance
>
} & P
}
}

0 comments on commit 00d7d87

Please sign in to comment.