diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index f3156332..ef08d3c6 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -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, EE extends string = string, PP = PublicProps, - RequiredProps = Readonly>, - OptionalProps = Readonly> + Props = Readonly>, + Defaults = ExtractDefaultPropTypes > = 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 diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 4d6291c9..ff499d36 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -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, 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 & { + 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 + Readonly, + Defaults, + false > > @@ -187,7 +208,18 @@ export type ComponentOptionsWithArrayProps< E extends EmitsOptions = EmitsOptions, EE extends string = string, Props = Readonly<{ [key in PropNames]?: any }> -> = ComponentOptionsBase & { +> = 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> -> = ComponentOptionsBase & { + Props = Readonly>, + Defaults = ExtractDefaultPropTypes +> = ComponentOptionsBase< + Props, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + EE, + Defaults +> & { props: PropsOptions & ThisType } & 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 { diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index a8bccd9a..0fde127a 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -63,18 +63,15 @@ type PropMethod = T extends (...args: any) => any // if i ? { new (): TConstructor; (): T; readonly prototype: TConstructor } // Create Function like constructor : never -type RequiredKeys = { - [K in keyof T]: T[K] extends - | { required: true } - | (MakeDefaultRequired extends true ? { default: any } : never) - ? K - : never +type RequiredKeys = { + [K in keyof T]: T[K] extends { required: true } | { default: any } ? K : never }[keyof T] -type OptionalKeys = Exclude< - keyof T, - RequiredKeys -> +type OptionalKeys = Exclude> + +type DefaultKeys = { + [K in keyof T]: T[K] extends { default: any } ? K : never +}[keyof T] type InferPropType = T extends null ? any // null & true would fail to infer @@ -86,12 +83,9 @@ type InferPropType = T extends null ? boolean : T extends Prop ? (unknown extends V ? D : V) : T -export type ExtractPropTypes< - O, - MakeDefaultRequired extends boolean = true -> = O extends object - ? { [K in RequiredKeys]: InferPropType } & - { [K in OptionalKeys]?: InferPropType } +export type ExtractPropTypes = O extends object + ? { [K in RequiredKeys]: InferPropType } & + { [K in OptionalKeys]?: InferPropType } : { [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 extends object + ? { [K in DefaultKeys]: InferPropType } + : {} + type NormalizedProp = | null | (PropOptions & { diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index 2a9fa747..e0e239a7 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -77,9 +77,11 @@ type MixinToOptionTypes = T extends ComponentOptionsBase< infer M, infer Mixin, infer Extends, - any + any, + any, + infer Defaults > - ? OptionTypesType

& + ? OptionTypesType

& IntersectionMixin & IntersectionMixin : never @@ -130,6 +132,8 @@ export type CreateComponentPublicInstance< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, PublicProps = P, + Defaults = {}, + MakeDefaultsOptional extends boolean = false, PublicMixin = IntersectionMixin & IntersectionMixin, PublicP = UnwrapMixinsType & EnsureNonVoid

, PublicB = UnwrapMixinsType & EnsureNonVoid, @@ -137,7 +141,9 @@ export type CreateComponentPublicInstance< PublicC extends ComputedOptions = UnwrapMixinsType & EnsureNonVoid, PublicM extends MethodOptions = UnwrapMixinsType & - EnsureNonVoid + EnsureNonVoid, + PublicDefaults = UnwrapMixinsType & + EnsureNonVoid > = ComponentPublicInstance< PublicP, PublicB, @@ -146,7 +152,9 @@ export type CreateComponentPublicInstance< PublicM, E, PublicProps, - ComponentOptionsBase + PublicDefaults, + MakeDefaultsOptional, + ComponentOptionsBase > // 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 + Defaults = {}, + MakeDefaultsOptional extends boolean = false, + Options = ComponentOptionsBase > = { $: ComponentInternalInstance $data: D - $props: P & PublicProps + $props: MakeDefaultsOptional extends true + ? Partial & Omit

+ : P & PublicProps $attrs: Data $refs: Data $slots: Slots diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 75dd69fb..26c27d54 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -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, diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx index 98f8018e..f201d660 100644 --- a/test-dts/defineComponent.test-d.tsx +++ b/test-dts/defineComponent.test-d.tsx @@ -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() + expectType() + + // mP1, mP2, p1, and p2 have default value. these are not required + expectType() // missing required props // @ts-expect-error - expectError() + expectError() + // missing required props from mixin + // @ts-expect-error + expectError() + // missing required props from extends + // @ts-expect-error + expectError() // wrong prop types // @ts-expect-error