fix(types/tsx): optional props from Mixin/Extends are treated as required (#2048)

This commit is contained in:
wonderful-panda 2020-09-16 23:09:35 +09:00 committed by GitHub
parent 7e68ddd354
commit 89e9ab8a2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 133 additions and 46 deletions

View 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

View 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 {

View 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 & {

View 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

View 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,

View 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