fix(types/tsx): optional props from Mixin/Extends are treated as required (#2048)
This commit is contained in:
parent
7e68ddd354
commit
89e9ab8a2a
@ -13,7 +13,11 @@ import {
|
|||||||
AllowedComponentProps,
|
AllowedComponentProps,
|
||||||
ComponentCustomProps
|
ComponentCustomProps
|
||||||
} from './component'
|
} from './component'
|
||||||
import { ExtractPropTypes, ComponentPropsOptions } from './componentProps'
|
import {
|
||||||
|
ExtractPropTypes,
|
||||||
|
ComponentPropsOptions,
|
||||||
|
ExtractDefaultPropTypes
|
||||||
|
} from './componentProps'
|
||||||
import { EmitsOptions } from './componentEmits'
|
import { EmitsOptions } from './componentEmits'
|
||||||
import { isFunction } from '@vue/shared'
|
import { isFunction } from '@vue/shared'
|
||||||
import { VNodeProps } from './vnode'
|
import { VNodeProps } from './vnode'
|
||||||
@ -37,11 +41,11 @@ export type DefineComponent<
|
|||||||
E extends EmitsOptions = Record<string, any>,
|
E extends EmitsOptions = Record<string, any>,
|
||||||
EE extends string = string,
|
EE extends string = string,
|
||||||
PP = PublicProps,
|
PP = PublicProps,
|
||||||
RequiredProps = Readonly<ExtractPropTypes<PropsOrPropOptions>>,
|
Props = Readonly<ExtractPropTypes<PropsOrPropOptions>>,
|
||||||
OptionalProps = Readonly<ExtractPropTypes<PropsOrPropOptions, false>>
|
Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>
|
||||||
> = ComponentPublicInstanceConstructor<
|
> = ComponentPublicInstanceConstructor<
|
||||||
CreateComponentPublicInstance<
|
CreateComponentPublicInstance<
|
||||||
OptionalProps,
|
Props,
|
||||||
RawBindings,
|
RawBindings,
|
||||||
D,
|
D,
|
||||||
C,
|
C,
|
||||||
@ -49,12 +53,14 @@ export type DefineComponent<
|
|||||||
Mixin,
|
Mixin,
|
||||||
Extends,
|
Extends,
|
||||||
E,
|
E,
|
||||||
PP & OptionalProps
|
PP & Props,
|
||||||
|
Defaults,
|
||||||
|
true
|
||||||
> &
|
> &
|
||||||
RequiredProps
|
Props
|
||||||
> &
|
> &
|
||||||
ComponentOptionsBase<
|
ComponentOptionsBase<
|
||||||
RequiredProps,
|
Props,
|
||||||
RawBindings,
|
RawBindings,
|
||||||
D,
|
D,
|
||||||
C,
|
C,
|
||||||
@ -62,7 +68,8 @@ export type DefineComponent<
|
|||||||
Mixin,
|
Mixin,
|
||||||
Extends,
|
Extends,
|
||||||
E,
|
E,
|
||||||
EE
|
EE,
|
||||||
|
Defaults
|
||||||
> &
|
> &
|
||||||
PP
|
PP
|
||||||
|
|
||||||
|
@ -42,7 +42,11 @@ import {
|
|||||||
WritableComputedOptions,
|
WritableComputedOptions,
|
||||||
toRaw
|
toRaw
|
||||||
} from '@vue/reactivity'
|
} from '@vue/reactivity'
|
||||||
import { ComponentObjectPropsOptions, ExtractPropTypes } from './componentProps'
|
import {
|
||||||
|
ComponentObjectPropsOptions,
|
||||||
|
ExtractPropTypes,
|
||||||
|
ExtractDefaultPropTypes
|
||||||
|
} from './componentProps'
|
||||||
import { EmitsOptions } from './componentEmits'
|
import { EmitsOptions } from './componentEmits'
|
||||||
import { Directive } from './directives'
|
import { Directive } from './directives'
|
||||||
import {
|
import {
|
||||||
@ -81,7 +85,8 @@ export interface ComponentOptionsBase<
|
|||||||
Mixin extends ComponentOptionsMixin,
|
Mixin extends ComponentOptionsMixin,
|
||||||
Extends extends ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin,
|
||||||
E extends EmitsOptions,
|
E extends EmitsOptions,
|
||||||
EE extends string = string
|
EE extends string = string,
|
||||||
|
Defaults = {}
|
||||||
>
|
>
|
||||||
extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
|
extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
|
||||||
ComponentInternalOptions,
|
ComponentInternalOptions,
|
||||||
@ -148,6 +153,8 @@ export interface ComponentOptionsBase<
|
|||||||
__isFragment?: never
|
__isFragment?: never
|
||||||
__isTeleport?: never
|
__isTeleport?: never
|
||||||
__isSuspense?: never
|
__isSuspense?: never
|
||||||
|
|
||||||
|
__defaults?: Defaults
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComponentOptionsWithoutProps<
|
export type ComponentOptionsWithoutProps<
|
||||||
@ -159,8 +166,20 @@ export type ComponentOptionsWithoutProps<
|
|||||||
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,
|
||||||
> = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E, EE> & {
|
Defaults = {}
|
||||||
|
> = ComponentOptionsBase<
|
||||||
|
Props,
|
||||||
|
RawBindings,
|
||||||
|
D,
|
||||||
|
C,
|
||||||
|
M,
|
||||||
|
Mixin,
|
||||||
|
Extends,
|
||||||
|
E,
|
||||||
|
EE,
|
||||||
|
Defaults
|
||||||
|
> & {
|
||||||
props?: undefined
|
props?: undefined
|
||||||
} & ThisType<
|
} & ThisType<
|
||||||
CreateComponentPublicInstance<
|
CreateComponentPublicInstance<
|
||||||
@ -172,7 +191,9 @@ export type ComponentOptionsWithoutProps<
|
|||||||
Mixin,
|
Mixin,
|
||||||
Extends,
|
Extends,
|
||||||
E,
|
E,
|
||||||
Readonly<Props>
|
Readonly<Props>,
|
||||||
|
Defaults,
|
||||||
|
false
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
|
|
||||||
@ -187,7 +208,18 @@ export type ComponentOptionsWithArrayProps<
|
|||||||
E extends EmitsOptions = EmitsOptions,
|
E extends EmitsOptions = EmitsOptions,
|
||||||
EE extends string = string,
|
EE extends string = string,
|
||||||
Props = Readonly<{ [key in PropNames]?: any }>
|
Props = Readonly<{ [key in PropNames]?: any }>
|
||||||
> = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E, EE> & {
|
> = ComponentOptionsBase<
|
||||||
|
Props,
|
||||||
|
RawBindings,
|
||||||
|
D,
|
||||||
|
C,
|
||||||
|
M,
|
||||||
|
Mixin,
|
||||||
|
Extends,
|
||||||
|
E,
|
||||||
|
EE,
|
||||||
|
{}
|
||||||
|
> & {
|
||||||
props: PropNames[]
|
props: PropNames[]
|
||||||
} & ThisType<
|
} & ThisType<
|
||||||
CreateComponentPublicInstance<
|
CreateComponentPublicInstance<
|
||||||
@ -212,8 +244,20 @@ 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,
|
||||||
Props = Readonly<ExtractPropTypes<PropsOptions>>
|
Props = Readonly<ExtractPropTypes<PropsOptions>>,
|
||||||
> = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E, EE> & {
|
Defaults = ExtractDefaultPropTypes<PropsOptions>
|
||||||
|
> = ComponentOptionsBase<
|
||||||
|
Props,
|
||||||
|
RawBindings,
|
||||||
|
D,
|
||||||
|
C,
|
||||||
|
M,
|
||||||
|
Mixin,
|
||||||
|
Extends,
|
||||||
|
E,
|
||||||
|
EE,
|
||||||
|
Defaults
|
||||||
|
> & {
|
||||||
props: PropsOptions & ThisType<void>
|
props: PropsOptions & ThisType<void>
|
||||||
} & ThisType<
|
} & ThisType<
|
||||||
CreateComponentPublicInstance<
|
CreateComponentPublicInstance<
|
||||||
@ -224,7 +268,10 @@ export type ComponentOptionsWithObjectProps<
|
|||||||
M,
|
M,
|
||||||
Mixin,
|
Mixin,
|
||||||
Extends,
|
Extends,
|
||||||
E
|
E,
|
||||||
|
Props,
|
||||||
|
Defaults,
|
||||||
|
false
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
|
|
||||||
@ -261,6 +308,7 @@ export type ComponentOptionsMixin = ComponentOptionsBase<
|
|||||||
any,
|
any,
|
||||||
any,
|
any,
|
||||||
any,
|
any,
|
||||||
|
any,
|
||||||
any
|
any
|
||||||
>
|
>
|
||||||
|
|
||||||
@ -347,20 +395,22 @@ interface LegacyOptions<
|
|||||||
delimiters?: [string, string]
|
delimiters?: [string, string]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M'
|
export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M' | 'Defaults'
|
||||||
|
|
||||||
export type OptionTypesType<
|
export type OptionTypesType<
|
||||||
P = {},
|
P = {},
|
||||||
B = {},
|
B = {},
|
||||||
D = {},
|
D = {},
|
||||||
C extends ComputedOptions = {},
|
C extends ComputedOptions = {},
|
||||||
M extends MethodOptions = {}
|
M extends MethodOptions = {},
|
||||||
|
Defaults = {}
|
||||||
> = {
|
> = {
|
||||||
P: P
|
P: P
|
||||||
B: B
|
B: B
|
||||||
D: D
|
D: D
|
||||||
C: C
|
C: C
|
||||||
M: M
|
M: M
|
||||||
|
Defaults: Defaults
|
||||||
}
|
}
|
||||||
|
|
||||||
const enum OptionTypes {
|
const enum OptionTypes {
|
||||||
|
@ -63,18 +63,15 @@ type PropMethod<T, TConstructor = any> = T extends (...args: any) => any // if i
|
|||||||
? { new (): TConstructor; (): T; readonly prototype: TConstructor } // Create Function like constructor
|
? { new (): TConstructor; (): T; readonly prototype: TConstructor } // Create Function like constructor
|
||||||
: never
|
: never
|
||||||
|
|
||||||
type RequiredKeys<T, MakeDefaultRequired> = {
|
type RequiredKeys<T> = {
|
||||||
[K in keyof T]: T[K] extends
|
[K in keyof T]: T[K] extends { required: true } | { default: any } ? K : never
|
||||||
| { required: true }
|
|
||||||
| (MakeDefaultRequired extends true ? { default: any } : never)
|
|
||||||
? K
|
|
||||||
: never
|
|
||||||
}[keyof T]
|
}[keyof T]
|
||||||
|
|
||||||
type OptionalKeys<T, MakeDefaultRequired> = Exclude<
|
type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>
|
||||||
keyof T,
|
|
||||||
RequiredKeys<T, MakeDefaultRequired>
|
type DefaultKeys<T> = {
|
||||||
>
|
[K in keyof T]: T[K] extends { default: any } ? K : never
|
||||||
|
}[keyof T]
|
||||||
|
|
||||||
type InferPropType<T> = T extends null
|
type InferPropType<T> = T extends null
|
||||||
? any // null & true would fail to infer
|
? any // null & true would fail to infer
|
||||||
@ -86,12 +83,9 @@ type InferPropType<T> = T extends null
|
|||||||
? boolean
|
? boolean
|
||||||
: T extends Prop<infer V, infer D> ? (unknown extends V ? D : V) : T
|
: T extends Prop<infer V, infer D> ? (unknown extends V ? D : V) : T
|
||||||
|
|
||||||
export type ExtractPropTypes<
|
export type ExtractPropTypes<O> = O extends object
|
||||||
O,
|
? { [K in RequiredKeys<O>]: InferPropType<O[K]> } &
|
||||||
MakeDefaultRequired extends boolean = true
|
{ [K in OptionalKeys<O>]?: InferPropType<O[K]> }
|
||||||
> = O extends object
|
|
||||||
? { [K in RequiredKeys<O, MakeDefaultRequired>]: InferPropType<O[K]> } &
|
|
||||||
{ [K in OptionalKeys<O, MakeDefaultRequired>]?: InferPropType<O[K]> }
|
|
||||||
: { [K in string]: any }
|
: { [K in string]: any }
|
||||||
|
|
||||||
const enum BooleanFlags {
|
const enum BooleanFlags {
|
||||||
@ -99,6 +93,11 @@ const enum BooleanFlags {
|
|||||||
shouldCastTrue
|
shouldCastTrue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extract props which defined with default from prop options
|
||||||
|
export type ExtractDefaultPropTypes<O> = O extends object
|
||||||
|
? { [K in DefaultKeys<O>]: InferPropType<O[K]> }
|
||||||
|
: {}
|
||||||
|
|
||||||
type NormalizedProp =
|
type NormalizedProp =
|
||||||
| null
|
| null
|
||||||
| (PropOptions & {
|
| (PropOptions & {
|
||||||
|
@ -77,9 +77,11 @@ type MixinToOptionTypes<T> = T extends ComponentOptionsBase<
|
|||||||
infer M,
|
infer M,
|
||||||
infer Mixin,
|
infer Mixin,
|
||||||
infer Extends,
|
infer Extends,
|
||||||
any
|
any,
|
||||||
|
any,
|
||||||
|
infer Defaults
|
||||||
>
|
>
|
||||||
? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}> &
|
? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}, Defaults & {}> &
|
||||||
IntersectionMixin<Mixin> &
|
IntersectionMixin<Mixin> &
|
||||||
IntersectionMixin<Extends>
|
IntersectionMixin<Extends>
|
||||||
: never
|
: never
|
||||||
@ -130,6 +132,8 @@ export type CreateComponentPublicInstance<
|
|||||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||||
E extends EmitsOptions = {},
|
E extends EmitsOptions = {},
|
||||||
PublicProps = P,
|
PublicProps = P,
|
||||||
|
Defaults = {},
|
||||||
|
MakeDefaultsOptional extends boolean = false,
|
||||||
PublicMixin = IntersectionMixin<Mixin> & IntersectionMixin<Extends>,
|
PublicMixin = IntersectionMixin<Mixin> & IntersectionMixin<Extends>,
|
||||||
PublicP = UnwrapMixinsType<PublicMixin, 'P'> & EnsureNonVoid<P>,
|
PublicP = UnwrapMixinsType<PublicMixin, 'P'> & EnsureNonVoid<P>,
|
||||||
PublicB = UnwrapMixinsType<PublicMixin, 'B'> & EnsureNonVoid<B>,
|
PublicB = UnwrapMixinsType<PublicMixin, 'B'> & EnsureNonVoid<B>,
|
||||||
@ -137,7 +141,9 @@ export type CreateComponentPublicInstance<
|
|||||||
PublicC extends ComputedOptions = UnwrapMixinsType<PublicMixin, 'C'> &
|
PublicC extends ComputedOptions = UnwrapMixinsType<PublicMixin, 'C'> &
|
||||||
EnsureNonVoid<C>,
|
EnsureNonVoid<C>,
|
||||||
PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> &
|
PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> &
|
||||||
EnsureNonVoid<M>
|
EnsureNonVoid<M>,
|
||||||
|
PublicDefaults = UnwrapMixinsType<PublicMixin, 'Defaults'> &
|
||||||
|
EnsureNonVoid<Defaults>
|
||||||
> = ComponentPublicInstance<
|
> = ComponentPublicInstance<
|
||||||
PublicP,
|
PublicP,
|
||||||
PublicB,
|
PublicB,
|
||||||
@ -146,7 +152,9 @@ export type CreateComponentPublicInstance<
|
|||||||
PublicM,
|
PublicM,
|
||||||
E,
|
E,
|
||||||
PublicProps,
|
PublicProps,
|
||||||
ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E>
|
PublicDefaults,
|
||||||
|
MakeDefaultsOptional,
|
||||||
|
ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E, string, Defaults>
|
||||||
>
|
>
|
||||||
|
|
||||||
// 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
|
||||||
@ -159,11 +167,15 @@ export type ComponentPublicInstance<
|
|||||||
M extends MethodOptions = {},
|
M extends MethodOptions = {},
|
||||||
E extends EmitsOptions = {},
|
E extends EmitsOptions = {},
|
||||||
PublicProps = P,
|
PublicProps = P,
|
||||||
Options = ComponentOptionsBase<any, any, any, any, any, any, any, any>
|
Defaults = {},
|
||||||
|
MakeDefaultsOptional extends boolean = false,
|
||||||
|
Options = ComponentOptionsBase<any, any, any, any, any, any, any, any, any>
|
||||||
> = {
|
> = {
|
||||||
$: ComponentInternalInstance
|
$: ComponentInternalInstance
|
||||||
$data: D
|
$data: D
|
||||||
$props: P & PublicProps
|
$props: MakeDefaultsOptional extends true
|
||||||
|
? Partial<Defaults> & Omit<P & PublicProps, keyof Defaults>
|
||||||
|
: P & PublicProps
|
||||||
$attrs: Data
|
$attrs: Data
|
||||||
$refs: Data
|
$refs: Data
|
||||||
$slots: Slots
|
$slots: Slots
|
||||||
|
@ -41,7 +41,7 @@ export {
|
|||||||
} from './apiLifecycle'
|
} from './apiLifecycle'
|
||||||
export { provide, inject } from './apiInject'
|
export { provide, inject } from './apiInject'
|
||||||
export { nextTick } from './scheduler'
|
export { nextTick } from './scheduler'
|
||||||
export { defineComponent, DefineComponent } from './apiDefineComponent'
|
export { defineComponent } from './apiDefineComponent'
|
||||||
export { defineAsyncComponent } from './apiAsyncComponent'
|
export { defineAsyncComponent } from './apiAsyncComponent'
|
||||||
|
|
||||||
// Advanced API ----------------------------------------------------------------
|
// Advanced API ----------------------------------------------------------------
|
||||||
@ -166,6 +166,7 @@ export {
|
|||||||
ComponentCustomProps,
|
ComponentCustomProps,
|
||||||
AllowedComponentProps
|
AllowedComponentProps
|
||||||
} from './component'
|
} from './component'
|
||||||
|
export { DefineComponent } from './apiDefineComponent'
|
||||||
export {
|
export {
|
||||||
ComponentOptions,
|
ComponentOptions,
|
||||||
ComponentOptionsMixin,
|
ComponentOptionsMixin,
|
||||||
@ -198,7 +199,8 @@ export {
|
|||||||
PropType,
|
PropType,
|
||||||
ComponentPropsOptions,
|
ComponentPropsOptions,
|
||||||
ComponentObjectPropsOptions,
|
ComponentObjectPropsOptions,
|
||||||
ExtractPropTypes
|
ExtractPropTypes,
|
||||||
|
ExtractDefaultPropTypes
|
||||||
} from './componentProps'
|
} from './componentProps'
|
||||||
export {
|
export {
|
||||||
Directive,
|
Directive,
|
||||||
|
@ -597,7 +597,11 @@ describe('extends with mixins', () => {
|
|||||||
type: String,
|
type: String,
|
||||||
default: 'mP1'
|
default: 'mP1'
|
||||||
},
|
},
|
||||||
mP2: Boolean
|
mP2: Boolean,
|
||||||
|
mP3: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -611,6 +615,10 @@ describe('extends with mixins', () => {
|
|||||||
p2: {
|
p2: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 2
|
default: 2
|
||||||
|
},
|
||||||
|
p3: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -663,11 +671,20 @@ describe('extends with mixins', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Test TSX
|
// Test TSX
|
||||||
expectType<JSX.Element>(<MyComponent mP1="p1" mP2 p1 p2={1} z={'z'} />)
|
expectType<JSX.Element>(<MyComponent mP1="p1" mP2 mP3 p1 p2={1} p3 z={'z'} />)
|
||||||
|
|
||||||
|
// mP1, mP2, p1, and p2 have default value. these are not required
|
||||||
|
expectType<JSX.Element>(<MyComponent mP3 p3 z={'z'} />)
|
||||||
|
|
||||||
// missing required props
|
// missing required props
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
expectError(<MyComponent />)
|
expectError(<MyComponent mP3 p3 /* z='z' */ />)
|
||||||
|
// missing required props from mixin
|
||||||
|
// @ts-expect-error
|
||||||
|
expectError(<MyComponent /* mP3 */ p3 z="z" />)
|
||||||
|
// missing required props from extends
|
||||||
|
// @ts-expect-error
|
||||||
|
expectError(<MyComponent mP3 /* p3 */ z="z" />)
|
||||||
|
|
||||||
// wrong prop types
|
// wrong prop types
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
Loading…
Reference in New Issue
Block a user