]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(types/tsx): optional props from Mixin/Extends are treated as required (#2048)
authorwonderful-panda <iwata0303@gmail.com>
Wed, 16 Sep 2020 14:09:35 +0000 (23:09 +0900)
committerGitHub <noreply@github.com>
Wed, 16 Sep 2020 14:09:35 +0000 (10:09 -0400)
packages/runtime-core/src/apiDefineComponent.ts
packages/runtime-core/src/componentOptions.ts
packages/runtime-core/src/componentProps.ts
packages/runtime-core/src/componentPublicInstance.ts
packages/runtime-core/src/index.ts
test-dts/defineComponent.test-d.tsx

index f3156332b457263dccef8ece5473b70528a977ed..ef08d3c6533894cfebfe0061ffa7af340d9909b5 100644 (file)
@@ -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
 
index 4d6291c95276f4a35aff16822e637a142df76d39..ff499d36cdc65fea9cf912ace53eb746c0927976 100644 (file)
@@ -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 {
index a8bccd9a5afba68011c0f121391a8268c02fc727..0fde127a28f76249369f1bbb012203e3a8daa554 100644 (file)
@@ -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 & {
index 2a9fa747339a5c2b929d8a992116230d2cafb4b7..e0e239a7997dcb538d89fe5b6d9b5e65fe84666b 100644 (file)
@@ -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
index 75dd69fbe8fbcea902d4c6a7710587efba34344c..26c27d544e6ee37edb47b62b1b8f95d7afb3e68f 100644 (file)
@@ -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,
index 98f8018e27195396d27adbe6367a7066754ff4b2..f201d660d87296af592423a470543274d6fbe2f5 100644 (file)
@@ -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