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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user