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,
|
||||
ComponentCustomProps
|
||||
} from './component'
|
||||
import { ExtractPropTypes, ComponentPropsOptions } from './componentProps'
|
||||
import {
|
||||
ExtractPropTypes,
|
||||
ComponentPropsOptions,
|
||||
ExtractDefaultPropTypes
|
||||
} from './componentProps'
|
||||
import { EmitsOptions } from './componentEmits'
|
||||
import { isFunction } from '@vue/shared'
|
||||
import { VNodeProps } from './vnode'
|
||||
@ -37,11 +41,11 @@ export type DefineComponent<
|
||||
E extends EmitsOptions = Record<string, any>,
|
||||
EE extends string = string,
|
||||
PP = PublicProps,
|
||||
RequiredProps = Readonly<ExtractPropTypes<PropsOrPropOptions>>,
|
||||
OptionalProps = Readonly<ExtractPropTypes<PropsOrPropOptions, false>>
|
||||
Props = Readonly<ExtractPropTypes<PropsOrPropOptions>>,
|
||||
Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>
|
||||
> = ComponentPublicInstanceConstructor<
|
||||
CreateComponentPublicInstance<
|
||||
OptionalProps,
|
||||
Props,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
@ -49,12 +53,14 @@ export type DefineComponent<
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
PP & OptionalProps
|
||||
PP & Props,
|
||||
Defaults,
|
||||
true
|
||||
> &
|
||||
RequiredProps
|
||||
Props
|
||||
> &
|
||||
ComponentOptionsBase<
|
||||
RequiredProps,
|
||||
Props,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
@ -62,7 +68,8 @@ export type DefineComponent<
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE
|
||||
EE,
|
||||
Defaults
|
||||
> &
|
||||
PP
|
||||
|
||||
|
@ -42,7 +42,11 @@ import {
|
||||
WritableComputedOptions,
|
||||
toRaw
|
||||
} from '@vue/reactivity'
|
||||
import { ComponentObjectPropsOptions, ExtractPropTypes } from './componentProps'
|
||||
import {
|
||||
ComponentObjectPropsOptions,
|
||||
ExtractPropTypes,
|
||||
ExtractDefaultPropTypes
|
||||
} from './componentProps'
|
||||
import { EmitsOptions } from './componentEmits'
|
||||
import { Directive } from './directives'
|
||||
import {
|
||||
@ -81,7 +85,8 @@ export interface ComponentOptionsBase<
|
||||
Mixin extends ComponentOptionsMixin,
|
||||
Extends extends ComponentOptionsMixin,
|
||||
E extends EmitsOptions,
|
||||
EE extends string = string
|
||||
EE extends string = string,
|
||||
Defaults = {}
|
||||
>
|
||||
extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
|
||||
ComponentInternalOptions,
|
||||
@ -148,6 +153,8 @@ export interface ComponentOptionsBase<
|
||||
__isFragment?: never
|
||||
__isTeleport?: never
|
||||
__isSuspense?: never
|
||||
|
||||
__defaults?: Defaults
|
||||
}
|
||||
|
||||
export type ComponentOptionsWithoutProps<
|
||||
@ -159,8 +166,20 @@ export type ComponentOptionsWithoutProps<
|
||||
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string
|
||||
> = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E, EE> & {
|
||||
EE extends string = string,
|
||||
Defaults = {}
|
||||
> = ComponentOptionsBase<
|
||||
Props,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE,
|
||||
Defaults
|
||||
> & {
|
||||
props?: undefined
|
||||
} & ThisType<
|
||||
CreateComponentPublicInstance<
|
||||
@ -172,7 +191,9 @@ export type ComponentOptionsWithoutProps<
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
Readonly<Props>
|
||||
Readonly<Props>,
|
||||
Defaults,
|
||||
false
|
||||
>
|
||||
>
|
||||
|
||||
@ -187,7 +208,18 @@ export type ComponentOptionsWithArrayProps<
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
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[]
|
||||
} & ThisType<
|
||||
CreateComponentPublicInstance<
|
||||
@ -212,8 +244,20 @@ export type ComponentOptionsWithObjectProps<
|
||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = EmitsOptions,
|
||||
EE extends string = string,
|
||||
Props = Readonly<ExtractPropTypes<PropsOptions>>
|
||||
> = ComponentOptionsBase<Props, RawBindings, D, C, M, Mixin, Extends, E, EE> & {
|
||||
Props = Readonly<ExtractPropTypes<PropsOptions>>,
|
||||
Defaults = ExtractDefaultPropTypes<PropsOptions>
|
||||
> = ComponentOptionsBase<
|
||||
Props,
|
||||
RawBindings,
|
||||
D,
|
||||
C,
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E,
|
||||
EE,
|
||||
Defaults
|
||||
> & {
|
||||
props: PropsOptions & ThisType<void>
|
||||
} & ThisType<
|
||||
CreateComponentPublicInstance<
|
||||
@ -224,7 +268,10 @@ export type ComponentOptionsWithObjectProps<
|
||||
M,
|
||||
Mixin,
|
||||
Extends,
|
||||
E
|
||||
E,
|
||||
Props,
|
||||
Defaults,
|
||||
false
|
||||
>
|
||||
>
|
||||
|
||||
@ -261,6 +308,7 @@ export type ComponentOptionsMixin = ComponentOptionsBase<
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any
|
||||
>
|
||||
|
||||
@ -347,20 +395,22 @@ interface LegacyOptions<
|
||||
delimiters?: [string, string]
|
||||
}
|
||||
|
||||
export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M'
|
||||
export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M' | 'Defaults'
|
||||
|
||||
export type OptionTypesType<
|
||||
P = {},
|
||||
B = {},
|
||||
D = {},
|
||||
C extends ComputedOptions = {},
|
||||
M extends MethodOptions = {}
|
||||
M extends MethodOptions = {},
|
||||
Defaults = {}
|
||||
> = {
|
||||
P: P
|
||||
B: B
|
||||
D: D
|
||||
C: C
|
||||
M: M
|
||||
Defaults: Defaults
|
||||
}
|
||||
|
||||
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
|
||||
: never
|
||||
|
||||
type RequiredKeys<T, MakeDefaultRequired> = {
|
||||
[K in keyof T]: T[K] extends
|
||||
| { required: true }
|
||||
| (MakeDefaultRequired extends true ? { default: any } : never)
|
||||
? K
|
||||
: never
|
||||
type RequiredKeys<T> = {
|
||||
[K in keyof T]: T[K] extends { required: true } | { default: any } ? K : never
|
||||
}[keyof T]
|
||||
|
||||
type OptionalKeys<T, MakeDefaultRequired> = Exclude<
|
||||
keyof T,
|
||||
RequiredKeys<T, MakeDefaultRequired>
|
||||
>
|
||||
type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>
|
||||
|
||||
type DefaultKeys<T> = {
|
||||
[K in keyof T]: T[K] extends { default: any } ? K : never
|
||||
}[keyof T]
|
||||
|
||||
type InferPropType<T> = T extends null
|
||||
? any // null & true would fail to infer
|
||||
@ -86,12 +83,9 @@ type InferPropType<T> = T extends null
|
||||
? boolean
|
||||
: T extends Prop<infer V, infer D> ? (unknown extends V ? D : V) : T
|
||||
|
||||
export type ExtractPropTypes<
|
||||
O,
|
||||
MakeDefaultRequired extends boolean = true
|
||||
> = O extends object
|
||||
? { [K in RequiredKeys<O, MakeDefaultRequired>]: InferPropType<O[K]> } &
|
||||
{ [K in OptionalKeys<O, MakeDefaultRequired>]?: InferPropType<O[K]> }
|
||||
export type ExtractPropTypes<O> = O extends object
|
||||
? { [K in RequiredKeys<O>]: InferPropType<O[K]> } &
|
||||
{ [K in OptionalKeys<O>]?: InferPropType<O[K]> }
|
||||
: { [K in string]: any }
|
||||
|
||||
const enum BooleanFlags {
|
||||
@ -99,6 +93,11 @@ const enum BooleanFlags {
|
||||
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 =
|
||||
| null
|
||||
| (PropOptions & {
|
||||
|
@ -77,9 +77,11 @@ type MixinToOptionTypes<T> = T extends ComponentOptionsBase<
|
||||
infer M,
|
||||
infer Mixin,
|
||||
infer Extends,
|
||||
any
|
||||
any,
|
||||
any,
|
||||
infer Defaults
|
||||
>
|
||||
? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}> &
|
||||
? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}, Defaults & {}> &
|
||||
IntersectionMixin<Mixin> &
|
||||
IntersectionMixin<Extends>
|
||||
: never
|
||||
@ -130,6 +132,8 @@ export type CreateComponentPublicInstance<
|
||||
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
|
||||
E extends EmitsOptions = {},
|
||||
PublicProps = P,
|
||||
Defaults = {},
|
||||
MakeDefaultsOptional extends boolean = false,
|
||||
PublicMixin = IntersectionMixin<Mixin> & IntersectionMixin<Extends>,
|
||||
PublicP = UnwrapMixinsType<PublicMixin, 'P'> & EnsureNonVoid<P>,
|
||||
PublicB = UnwrapMixinsType<PublicMixin, 'B'> & EnsureNonVoid<B>,
|
||||
@ -137,7 +141,9 @@ export type CreateComponentPublicInstance<
|
||||
PublicC extends ComputedOptions = UnwrapMixinsType<PublicMixin, 'C'> &
|
||||
EnsureNonVoid<C>,
|
||||
PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> &
|
||||
EnsureNonVoid<M>
|
||||
EnsureNonVoid<M>,
|
||||
PublicDefaults = UnwrapMixinsType<PublicMixin, 'Defaults'> &
|
||||
EnsureNonVoid<Defaults>
|
||||
> = ComponentPublicInstance<
|
||||
PublicP,
|
||||
PublicB,
|
||||
@ -146,7 +152,9 @@ export type CreateComponentPublicInstance<
|
||||
PublicM,
|
||||
E,
|
||||
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
|
||||
@ -159,11 +167,15 @@ export type ComponentPublicInstance<
|
||||
M extends MethodOptions = {},
|
||||
E extends EmitsOptions = {},
|
||||
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
|
||||
$data: D
|
||||
$props: P & PublicProps
|
||||
$props: MakeDefaultsOptional extends true
|
||||
? Partial<Defaults> & Omit<P & PublicProps, keyof Defaults>
|
||||
: P & PublicProps
|
||||
$attrs: Data
|
||||
$refs: Data
|
||||
$slots: Slots
|
||||
|
@ -41,7 +41,7 @@ export {
|
||||
} from './apiLifecycle'
|
||||
export { provide, inject } from './apiInject'
|
||||
export { nextTick } from './scheduler'
|
||||
export { defineComponent, DefineComponent } from './apiDefineComponent'
|
||||
export { defineComponent } from './apiDefineComponent'
|
||||
export { defineAsyncComponent } from './apiAsyncComponent'
|
||||
|
||||
// Advanced API ----------------------------------------------------------------
|
||||
@ -166,6 +166,7 @@ export {
|
||||
ComponentCustomProps,
|
||||
AllowedComponentProps
|
||||
} from './component'
|
||||
export { DefineComponent } from './apiDefineComponent'
|
||||
export {
|
||||
ComponentOptions,
|
||||
ComponentOptionsMixin,
|
||||
@ -198,7 +199,8 @@ export {
|
||||
PropType,
|
||||
ComponentPropsOptions,
|
||||
ComponentObjectPropsOptions,
|
||||
ExtractPropTypes
|
||||
ExtractPropTypes,
|
||||
ExtractDefaultPropTypes
|
||||
} from './componentProps'
|
||||
export {
|
||||
Directive,
|
||||
|
@ -597,7 +597,11 @@ describe('extends with mixins', () => {
|
||||
type: String,
|
||||
default: 'mP1'
|
||||
},
|
||||
mP2: Boolean
|
||||
mP2: Boolean,
|
||||
mP3: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -611,6 +615,10 @@ describe('extends with mixins', () => {
|
||||
p2: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
p3: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -663,11 +671,20 @@ describe('extends with mixins', () => {
|
||||
})
|
||||
|
||||
// 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
|
||||
// @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
|
||||
// @ts-expect-error
|
||||
|
Loading…
Reference in New Issue
Block a user