Skip to content

Commit

Permalink
types(defineComponent): support for GlobalComponents, typed Directive…
Browse files Browse the repository at this point in the history
…s and respect `expose` on defineComponent (#3399)

close #3367
  • Loading branch information
pikax authored Apr 25, 2024
1 parent 0e6e3c7 commit 4cc9ca8
Show file tree
Hide file tree
Showing 15 changed files with 533 additions and 86 deletions.
12 changes: 11 additions & 1 deletion packages/dts-test/componentTypeExtensions.test-d.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { defineComponent } from 'vue'
import { type DefineComponent, type Directive, defineComponent } from 'vue'
import { expectType } from './utils'

declare module 'vue' {
interface ComponentCustomOptions {
test?(n: number): void
}

interface GlobalDirectives {
test: Directive
}

interface GlobalComponents {
RouterView: DefineComponent<{}>
}

interface ComponentCustomProperties {
state?: 'stopped' | 'running'
}
Expand Down Expand Up @@ -46,6 +54,8 @@ export const Custom = defineComponent({
},
})

expectType<Directive>(Custom.directives!.test)
expectType<DefineComponent<{}>>(Custom.components!.RouterView)
expectType<JSX.Element>(<Custom baz={1} />)
expectType<JSX.Element>(<Custom custom={1} baz={1} />)
expectType<JSX.Element>(<Custom bar="bar" baz={1} />)
Expand Down
90 changes: 90 additions & 0 deletions packages/dts-test/defineComponent.test-d.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1501,18 +1501,108 @@ describe('should work when props type is incompatible with setup returned type '

describe('withKeys and withModifiers as pro', () => {
const onKeydown = withKeys(e => {}, [''])
// @ts-expect-error invalid modifiers
const onClick = withModifiers(e => {}, [''])
;<input onKeydown={onKeydown} onClick={onClick} />
})

// #3367 expose components types
describe('expose component types', () => {
const child = defineComponent({
props: {
a: String,
},
})

const parent = defineComponent({
components: {
child,
child2: {
template: `<div></div>`,
},
},
})

expectType<typeof child>(parent.components!.child)
expectType<Component>(parent.components!.child2)

// global components
expectType<Readonly<KeepAliveProps>>(
new parent.components!.KeepAlive().$props,
)
expectType<Readonly<KeepAliveProps>>(new child.components!.KeepAlive().$props)

// runtime-dom components
expectType<Readonly<TransitionProps>>(
new parent.components!.Transition().$props,
)
expectType<Readonly<TransitionProps>>(
new child.components!.Transition().$props,
)
})

describe('directive typing', () => {
const customDirective: Directive = {
created(_) {},
}

const comp = defineComponent({
props: {
a: String,
},
directives: {
customDirective,
localDirective: {
created(_, { arg }) {
expectType<string | undefined>(arg)
},
},
},
})

expectType<typeof customDirective>(comp.directives!.customDirective)
expectType<Directive>(comp.directives!.localDirective)

// global directive
expectType<typeof vShow>(comp.directives!.vShow)
})

describe('expose typing', () => {
const Comp = defineComponent({
expose: ['a', 'b'],
props: {
some: String,
},
data() {
return { a: 1, b: '2', c: 1 }
},
})

expectType<Array<'a' | 'b'>>(Comp.expose!)

const vm = new Comp()
// internal should still be exposed
vm.$props

expectType<number>(vm.a)
expectType<string>(vm.b)

// @ts-expect-error shouldn't be exposed
vm.c
})

import type {
AllowedComponentProps,
ComponentCustomProps,
ComponentOptionsMixin,
DefineComponent,
Directive,
EmitsOptions,
ExtractPropTypes,
KeepAliveProps,
TransitionProps,
VNodeProps,
vShow,
} from 'vue'

// code generated by tsc / vue-tsc, make sure this continues to work
Expand Down
58 changes: 58 additions & 0 deletions packages/dts-test/directives.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { type Directive, type ObjectDirective, vModelText } from 'vue'
import { describe, expectType } from './utils'

type ExtractBinding<T> = T extends (
el: any,
binding: infer B,
vnode: any,
prev: any,
) => any
? B
: never

declare function testDirective<
Value,
Modifiers extends string = string,
Arg extends string = string,
>(): ExtractBinding<Directive<any, Value, Modifiers, Arg>>

describe('vmodel', () => {
expectType<ObjectDirective<any, any, 'trim' | 'number' | 'lazy', string>>(
vModelText,
)
// @ts-expect-error
expectType<ObjectDirective<any, any, 'not-valid', string>>(vModelText)
})

describe('custom', () => {
expectType<{
value: number
oldValue: number | null
arg?: 'Arg'
modifiers: Record<'a' | 'b', boolean>
}>(testDirective<number, 'a' | 'b', 'Arg'>())

expectType<{
value: number
oldValue: number | null
arg?: 'Arg'
modifiers: Record<'a' | 'b', boolean>
// @ts-expect-error
}>(testDirective<number, 'a', 'Arg'>())

expectType<{
value: number
oldValue: number | null
arg?: 'Arg'
modifiers: Record<'a' | 'b', boolean>
// @ts-expect-error
}>(testDirective<number, 'a' | 'b', 'Argx'>())

expectType<{
value: number
oldValue: number | null
arg?: 'Arg'
modifiers: Record<'a' | 'b', boolean>
// @ts-expect-error
}>(testDirective<string, 'a' | 'b', 'Arg'>())
})
72 changes: 62 additions & 10 deletions packages/runtime-core/src/apiDefineComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import type {
ComponentOptionsWithArrayProps,
ComponentOptionsWithObjectProps,
ComponentOptionsWithoutProps,
ComponentProvideOptions,
ComputedOptions,
MethodOptions,
RenderFunction,
} from './componentOptions'
import type {
AllowedComponentProps,
Component,
ComponentCustomProps,
GlobalComponents,
GlobalDirectives,
SetupContext,
} from './component'
import type {
Expand All @@ -29,6 +33,7 @@ import type {
CreateComponentPublicInstance,
} from './componentPublicInstance'
import type { SlotsType } from './componentSlots'
import type { Directive } from './directives'

export type PublicProps = VNodeProps &
AllowedComponentProps &
Expand All @@ -55,6 +60,10 @@ export type DefineComponent<
Props = ResolveProps<PropsOrPropOptions, E>,
Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>,
S extends SlotsType = {},
LC extends Record<string, Component> = {},
Directives extends Record<string, Directive> = {},
Exposed extends string = string,
Provide extends ComponentProvideOptions = ComponentProvideOptions,
> = ComponentPublicInstanceConstructor<
CreateComponentPublicInstance<
Props,
Expand All @@ -69,7 +78,10 @@ export type DefineComponent<
Defaults,
true,
{},
S
S,
LC & GlobalComponents,
Directives & GlobalDirectives,
Exposed
>
> &
ComponentOptionsBase<
Expand All @@ -85,7 +97,11 @@ export type DefineComponent<
Defaults,
{},
string,
S
S,
LC & GlobalComponents,
Directives & GlobalDirectives,
Exposed,
Provide
> &
PP

Expand Down Expand Up @@ -166,9 +182,13 @@ export function defineComponent<
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = {},
EE extends string = string,
S extends SlotsType = {},
I extends ComponentInjectOptions = {},
II extends string = string,
S extends SlotsType = {},
LC extends Record<string, Component> = {},
Directives extends Record<string, Directive> = {},
Exposed extends string = string,
Provide extends ComponentProvideOptions = ComponentProvideOptions,
>(
options: ComponentOptionsWithoutProps<
Props,
Expand All @@ -182,7 +202,11 @@ export function defineComponent<
EE,
I,
II,
S
S,
LC,
Directives,
Exposed,
Provide
>,
): DefineComponent<
Props,
Expand All @@ -197,7 +221,11 @@ export function defineComponent<
PublicProps,
ResolveProps<Props, E>,
ExtractDefaultPropTypes<Props>,
S
S,
LC,
Directives,
Exposed,
Provide
>

// overload 3: object format with array props declaration
Expand All @@ -216,6 +244,10 @@ export function defineComponent<
S extends SlotsType = {},
I extends ComponentInjectOptions = {},
II extends string = string,
LC extends Record<string, Component> = {},
Directives extends Record<string, Directive> = {},
Exposed extends string = string,
Provide extends ComponentProvideOptions = ComponentProvideOptions,
Props = Readonly<{ [key in PropNames]?: any }>,
>(
options: ComponentOptionsWithArrayProps<
Expand All @@ -230,7 +262,11 @@ export function defineComponent<
EE,
I,
II,
S
S,
LC,
Directives,
Exposed,
Provide
>,
): DefineComponent<
Props,
Expand All @@ -245,7 +281,11 @@ export function defineComponent<
PublicProps,
ResolveProps<Props, E>,
ExtractDefaultPropTypes<Props>,
S
S,
LC,
Directives,
Exposed,
Provide
>

// overload 4: object format with object props declaration
Expand All @@ -262,9 +302,13 @@ export function defineComponent<
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = {},
EE extends string = string,
S extends SlotsType = {},
I extends ComponentInjectOptions = {},
II extends string = string,
S extends SlotsType = {},
LC extends Record<string, Component> = {},
Directives extends Record<string, Directive> = {},
Exposed extends string = string,
Provide extends ComponentProvideOptions = ComponentProvideOptions,
>(
options: ComponentOptionsWithObjectProps<
PropsOptions,
Expand All @@ -278,7 +322,11 @@ export function defineComponent<
EE,
I,
II,
S
S,
LC,
Directives,
Exposed,
Provide
>,
): DefineComponent<
PropsOptions,
Expand All @@ -293,7 +341,11 @@ export function defineComponent<
PublicProps,
ResolveProps<PropsOptions, E>,
ExtractDefaultPropTypes<PropsOptions>,
S
S,
LC,
Directives,
Exposed,
Provide
>

// implementation, close to no-op
Expand Down
Loading

0 comments on commit 4cc9ca8

Please sign in to comment.