fix(types): preserve and expose original options on defineComponent return type (#5416)

fix #3796
This commit is contained in:
Zclhlmgqzc 2022-05-06 17:44:05 +08:00 committed by GitHub
parent 32e53bfd47
commit 98b821d94a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 242 additions and 156 deletions

View File

@ -111,7 +111,7 @@ export function defineAsyncComponent<
) )
} }
return defineComponent({ return defineComponent<{}>({
name: 'AsyncComponentWrapper', name: 'AsyncComponentWrapper',
__asyncLoader: load, __asyncLoader: load,

View File

@ -6,7 +6,8 @@ import {
ComponentOptionsWithObjectProps, ComponentOptionsWithObjectProps,
ComponentOptionsMixin, ComponentOptionsMixin,
RenderFunction, RenderFunction,
ComponentOptionsBase ComponentOptionsBase,
ComponentProvideOptions
} from './componentOptions' } from './componentOptions'
import { import {
SetupContext, SetupContext,
@ -40,6 +41,8 @@ export type DefineComponent<
Extends extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = {}, E extends EmitsOptions = {},
EE extends string = string, EE extends string = string,
Provide extends ComponentProvideOptions = ComponentProvideOptions,
RawOptions extends {} = {},
PP = PublicProps, PP = PublicProps,
Props = Readonly< Props = Readonly<
PropsOrPropOptions extends ComponentPropsOptions PropsOrPropOptions extends ComponentPropsOptions
@ -48,22 +51,23 @@ export type DefineComponent<
> & > &
({} extends E ? {} : EmitsToProps<E>), ({} extends E ? {} : EmitsToProps<E>),
Defaults = ExtractDefaultPropTypes<PropsOrPropOptions> Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>
> = ComponentPublicInstanceConstructor< > = RawOptions &
CreateComponentPublicInstance< ComponentPublicInstanceConstructor<
Props, CreateComponentPublicInstance<
RawBindings, Props,
D, RawBindings,
C, D,
M, C,
Mixin, M,
Extends, Mixin,
E, Extends,
PP & Props, E,
Defaults, PP & Props,
true Defaults,
true
> &
Props
> & > &
Props
> &
ComponentOptionsBase< ComponentOptionsBase<
Props, Props,
RawBindings, RawBindings,
@ -74,7 +78,8 @@ export type DefineComponent<
Extends, Extends,
E, E,
EE, EE,
Defaults Defaults,
Provide
> & > &
PP PP
@ -104,20 +109,36 @@ export function defineComponent<
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = EmitsOptions, E extends EmitsOptions = EmitsOptions,
EE extends string = string EE extends string = string,
Provide extends ComponentProvideOptions = ComponentProvideOptions,
Options extends {} = {}
>( >(
options: ComponentOptionsWithoutProps< options: Options &
Props, ComponentOptionsWithoutProps<
RawBindings, Props,
D, RawBindings,
C, D,
M, C,
Mixin, M,
Extends, Mixin,
E, Extends,
EE E,
> EE,
): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE> Provide
>
): DefineComponent<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE,
Provide,
Options
>
// overload 3: object format with array props declaration // overload 3: object format with array props declaration
// props inferred as { [key in PropNames]?: any } // props inferred as { [key in PropNames]?: any }
@ -131,19 +152,23 @@ export function defineComponent<
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>, E extends EmitsOptions = Record<string, any>,
EE extends string = string EE extends string = string,
Provide extends ComponentProvideOptions = ComponentProvideOptions,
Options extends {} = {}
>( >(
options: ComponentOptionsWithArrayProps< options: Options &
PropNames, ComponentOptionsWithArrayProps<
RawBindings, PropNames,
D, RawBindings,
C, D,
M, C,
Mixin, M,
Extends, Mixin,
E, Extends,
EE E,
> EE,
Provide
>
): DefineComponent< ): DefineComponent<
Readonly<{ [key in PropNames]?: any }>, Readonly<{ [key in PropNames]?: any }>,
RawBindings, RawBindings,
@ -153,7 +178,9 @@ export function defineComponent<
Mixin, Mixin,
Extends, Extends,
E, E,
EE EE,
Provide,
Options
> >
// overload 4: object format with object props declaration // overload 4: object format with object props declaration
@ -169,20 +196,36 @@ export function defineComponent<
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>, E extends EmitsOptions = Record<string, any>,
EE extends string = string EE extends string = string,
Provide extends ComponentProvideOptions = ComponentProvideOptions,
Options extends {} = {}
>( >(
options: ComponentOptionsWithObjectProps< options: Options &
PropsOptions, ComponentOptionsWithObjectProps<
RawBindings, PropsOptions,
D, RawBindings,
C, D,
M, C,
Mixin, M,
Extends, Mixin,
E, Extends,
EE E,
> EE,
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE> Provide
>
): DefineComponent<
PropsOptions,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE,
Provide,
Options
>
// implementation, close to no-op // implementation, close to no-op
export function defineComponent(options: unknown) { export function defineComponent(options: unknown) {

View File

@ -117,8 +117,9 @@ export interface ComponentOptionsBase<
Extends extends ComponentOptionsMixin, Extends extends ComponentOptionsMixin,
E extends EmitsOptions, E extends EmitsOptions,
EE extends string = string, EE extends string = string,
Defaults = {} Defaults = {},
> extends LegacyOptions<Props, D, C, M, Mixin, Extends>, Provide extends ComponentProvideOptions = ComponentProvideOptions
> extends LegacyOptions<Props, D, C, M, Mixin, Extends, Provide>,
ComponentInternalOptions, ComponentInternalOptions,
ComponentCustomOptions { ComponentCustomOptions {
setup?: ( setup?: (
@ -224,6 +225,7 @@ export type ComponentOptionsWithoutProps<
Extends extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = EmitsOptions, E extends EmitsOptions = EmitsOptions,
EE extends string = string, EE extends string = string,
Provide extends ComponentProvideOptions = ComponentProvideOptions,
PE = Props & EmitsToProps<E> PE = Props & EmitsToProps<E>
> = ComponentOptionsBase< > = ComponentOptionsBase<
PE, PE,
@ -235,7 +237,8 @@ export type ComponentOptionsWithoutProps<
Extends, Extends,
E, E,
EE, EE,
{} {},
Provide
> & { > & {
props?: undefined props?: undefined
} & ThisType< } & ThisType<
@ -252,6 +255,7 @@ export type ComponentOptionsWithArrayProps<
Extends extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = EmitsOptions, E extends EmitsOptions = EmitsOptions,
EE extends string = string, EE extends string = string,
Provide extends ComponentProvideOptions = ComponentProvideOptions,
Props = Readonly<{ [key in PropNames]?: any }> & EmitsToProps<E> Props = Readonly<{ [key in PropNames]?: any }> & EmitsToProps<E>
> = ComponentOptionsBase< > = ComponentOptionsBase<
Props, Props,
@ -263,7 +267,8 @@ export type ComponentOptionsWithArrayProps<
Extends, Extends,
E, E,
EE, EE,
{} {},
Provide
> & { > & {
props: PropNames[] props: PropNames[]
} & ThisType< } & ThisType<
@ -289,6 +294,7 @@ export type ComponentOptionsWithObjectProps<
Extends extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = EmitsOptions, E extends EmitsOptions = EmitsOptions,
EE extends string = string, EE extends string = string,
Provide extends ComponentProvideOptions = ComponentProvideOptions,
Props = Readonly<ExtractPropTypes<PropsOptions>> & EmitsToProps<E>, Props = Readonly<ExtractPropTypes<PropsOptions>> & EmitsToProps<E>,
Defaults = ExtractDefaultPropTypes<PropsOptions> Defaults = ExtractDefaultPropTypes<PropsOptions>
> = ComponentOptionsBase< > = ComponentOptionsBase<
@ -301,7 +307,8 @@ export type ComponentOptionsWithObjectProps<
Extends, Extends,
E, E,
EE, EE,
Defaults Defaults,
Provide
> & { > & {
props: PropsOptions & ThisType<void> props: PropsOptions & ThisType<void>
} & ThisType< } & ThisType<
@ -384,6 +391,10 @@ type ComponentWatchOptionItem = WatchOptionItem | WatchOptionItem[]
type ComponentWatchOptions = Record<string, ComponentWatchOptionItem> type ComponentWatchOptions = Record<string, ComponentWatchOptionItem>
export type ComponentProvideOptions = ObjectProvideOptions | Function
type ObjectProvideOptions = Record<string | symbol, unknown>
type ComponentInjectOptions = string[] | ObjectInjectOptions type ComponentInjectOptions = string[] | ObjectInjectOptions
type ObjectInjectOptions = Record< type ObjectInjectOptions = Record<
@ -397,7 +408,8 @@ interface LegacyOptions<
C extends ComputedOptions, C extends ComputedOptions,
M extends MethodOptions, M extends MethodOptions,
Mixin extends ComponentOptionsMixin, Mixin extends ComponentOptionsMixin,
Extends extends ComponentOptionsMixin Extends extends ComponentOptionsMixin,
Provide extends ComponentProvideOptions = ComponentProvideOptions
> { > {
compatConfig?: CompatConfig compatConfig?: CompatConfig
@ -431,7 +443,7 @@ interface LegacyOptions<
computed?: C computed?: C
methods?: M methods?: M
watch?: ComponentWatchOptions watch?: ComponentWatchOptions
provide?: Data | Function provide?: Provide
inject?: ComponentInjectOptions inject?: ComponentInjectOptions
// assets // assets

View File

@ -34,7 +34,8 @@ import {
OptionTypesKeys, OptionTypesKeys,
resolveMergedOptions, resolveMergedOptions,
shouldCacheAccess, shouldCacheAccess,
MergedComponentOptionsOverride MergedComponentOptionsOverride,
ComponentProvideOptions
} from './componentOptions' } from './componentOptions'
import { EmitsOptions, EmitFn } from './componentEmits' import { EmitsOptions, EmitFn } from './componentEmits'
import { Slots } from './componentSlots' import { Slots } from './componentSlots'
@ -150,7 +151,8 @@ export type CreateComponentPublicInstance<
PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> & PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> &
EnsureNonVoid<M>, EnsureNonVoid<M>,
PublicDefaults = UnwrapMixinsType<PublicMixin, 'Defaults'> & PublicDefaults = UnwrapMixinsType<PublicMixin, 'Defaults'> &
EnsureNonVoid<Defaults> EnsureNonVoid<Defaults>,
Provide extends ComponentProvideOptions = ComponentProvideOptions
> = ComponentPublicInstance< > = ComponentPublicInstance<
PublicP, PublicP,
PublicB, PublicB,
@ -161,7 +163,19 @@ export type CreateComponentPublicInstance<
PublicProps, PublicProps,
PublicDefaults, PublicDefaults,
MakeDefaultsOptional, MakeDefaultsOptional,
ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E, string, Defaults> ComponentOptionsBase<
P,
B,
D,
C,
M,
Mixin,
Extends,
E,
string,
Defaults,
Provide
>
> >
// public properties exposed on the proxy, which is used as the render context // public properties exposed on the proxy, which is used as the render context

View File

@ -49,96 +49,98 @@ describe('with object props', () => {
type GT = string & { __brand: unknown } type GT = string & { __brand: unknown }
const MyComponent = defineComponent({ const props = {
props: { a: Number,
a: Number, // required should make property non-void
// required should make property non-void b: {
b: { type: String,
type: String, required: true as true
required: true
},
e: Function,
h: Boolean,
j: Function as PropType<undefined | (() => string | undefined)>,
// default value should infer type and make it non-void
bb: {
default: 'hello'
},
bbb: {
// Note: default function value requires arrow syntax + explicit
// annotation
default: (props: any) => (props.bb as string) || 'foo'
},
bbbb: {
type: String,
default: undefined
},
bbbbb: {
type: String,
default: () => undefined
},
// explicit type casting
cc: Array as PropType<string[]>,
// required + type casting
dd: {
type: Object as PropType<{ n: 1 }>,
required: true
},
// return type
ee: Function as PropType<() => string>,
// arguments + object return
ff: Function as PropType<(a: number, b: string) => { a: boolean }>,
// explicit type casting with constructor
ccc: Array as () => string[],
// required + constructor type casting
ddd: {
type: Array as () => string[],
required: true
},
// required + object return
eee: {
type: Function as PropType<() => { a: string }>,
required: true
},
// required + arguments + object return
fff: {
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
required: true
},
hhh: {
type: Boolean,
required: true
},
// default + type casting
ggg: {
type: String as PropType<'foo' | 'bar'>,
default: 'foo'
},
// default + function
ffff: {
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
default: (a: number, b: string) => ({ a: a > +b })
},
// union + function with different return types
iii: Function as PropType<(() => string) | (() => number)>,
// union + function with different args & same return type
jjj: {
type: Function as PropType<
((arg1: string) => string) | ((arg1: string, arg2: string) => string)
>,
required: true
},
kkk: null,
validated: {
type: String,
// validator requires explicit annotation
validator: (val: unknown) => val !== ''
},
date: Date,
l: [Date],
ll: [Date, Number],
lll: [String, Number]
}, },
e: Function,
h: Boolean,
j: Function as PropType<undefined | (() => string | undefined)>,
// default value should infer type and make it non-void
bb: {
default: 'hello'
},
bbb: {
// Note: default function value requires arrow syntax + explicit
// annotation
default: (props: any) => (props.bb as string) || 'foo'
},
bbbb: {
type: String,
default: undefined
},
bbbbb: {
type: String,
default: () => undefined
},
// explicit type casting
cc: Array as PropType<string[]>,
// required + type casting
dd: {
type: Object as PropType<{ n: 1 }>,
required: true as true
},
// return type
ee: Function as PropType<() => string>,
// arguments + object return
ff: Function as PropType<(a: number, b: string) => { a: boolean }>,
// explicit type casting with constructor
ccc: Array as () => string[],
// required + constructor type casting
ddd: {
type: Array as () => string[],
required: true as true
},
// required + object return
eee: {
type: Function as PropType<() => { a: string }>,
required: true as true
},
// required + arguments + object return
fff: {
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
required: true as true
},
hhh: {
type: Boolean,
required: true as true
},
// default + type casting
ggg: {
type: String as PropType<'foo' | 'bar'>,
default: 'foo'
},
// default + function
ffff: {
type: Function as PropType<(a: number, b: string) => { a: boolean }>,
default: (a: number, b: string) => ({ a: a > +b })
},
// union + function with different return types
iii: Function as PropType<(() => string) | (() => number)>,
// union + function with different args & same return type
jjj: {
type: Function as PropType<
((arg1: string) => string) | ((arg1: string, arg2: string) => string)
>,
required: true as true
},
kkk: null,
validated: {
type: String,
// validator requires explicit annotation
validator: (val: unknown) => val !== ''
},
date: Date,
l: [Date],
ll: [Date, Number],
lll: [String, Number]
}
const MyComponent = defineComponent({
props,
setup(props) { setup(props) {
// type assertion. See https://github.com/SamVerschueren/tsd // type assertion. See https://github.com/SamVerschueren/tsd
expectType<ExpectedProps['a']>(props.a) expectType<ExpectedProps['a']>(props.a)
@ -188,6 +190,9 @@ describe('with object props', () => {
}) })
} }
}, },
provide() {
return {}
},
render() { render() {
const props = this.$props const props = this.$props
expectType<ExpectedProps['a']>(props.a) expectType<ExpectedProps['a']>(props.a)
@ -258,6 +263,18 @@ describe('with object props', () => {
expectType<Component>(MyComponent) expectType<Component>(MyComponent)
expectType<typeof props>(MyComponent.props)
// @ts-expect-error it should be an object and not any
expectError<[]>(MyComponent.props)
expectType<() => {}>(MyComponent.provide)
// @ts-expect-error
expectError<[]>(MyComponent.provide)
expectType<() => null>(MyComponent.render)
expectType<Function | undefined>(defineComponent({}).render)
// Test TSX // Test TSX
expectType<JSX.Element>( expectType<JSX.Element>(
<MyComponent <MyComponent