mirror of
https://github.com/vuejs/core.git
synced 2026-09-28 05:03:05 +08:00
fix(types): keep optional props when a runtime prop uses a generic PropType (#15523)
close #9546
This commit is contained in:
@@ -299,6 +299,40 @@ describe('defineProps w/ runtime declaration', () => {
|
||||
props2.baz
|
||||
})
|
||||
|
||||
describe('defineProps w/ runtime declaration + generic PropType', <T extends
|
||||
Record<string, any>>() => {
|
||||
// #9546
|
||||
const props = defineProps({
|
||||
foo: {
|
||||
type: Object as PropType<T>,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
bar: {
|
||||
type: Object as PropType<T>,
|
||||
required: true,
|
||||
},
|
||||
baz: Object as PropType<T>,
|
||||
qux: String,
|
||||
})
|
||||
expectType<'foo' | 'bar' | 'baz' | 'qux'>(null! as keyof typeof props)
|
||||
expectType<T | null>(props.foo)
|
||||
expectType<T>(props.bar)
|
||||
expectType<T | undefined>(props.baz)
|
||||
expectType<string | undefined>(props.qux)
|
||||
|
||||
// @ts-expect-error should not be a string
|
||||
expectType<string>(props.foo)
|
||||
// @ts-expect-error should not be a string
|
||||
expectType<string>(props.bar)
|
||||
// @ts-expect-error should not be a string
|
||||
expectType<string>(props.baz)
|
||||
|
||||
// #9277: props should be usable where `T` is expected
|
||||
const use = (p: { bar: T }) => p
|
||||
use(props)
|
||||
})
|
||||
|
||||
describe('defineEmits w/ type declaration', () => {
|
||||
const emit = defineEmits<(e: 'change') => void>()
|
||||
emit('change')
|
||||
|
||||
@@ -72,11 +72,16 @@ export type PropType<T> = PropConstructor<T> | (PropConstructor<T> | null)[]
|
||||
type PropConstructor<T = any> =
|
||||
{ new (...args: any[]): T & {} } | { (): T } | PropMethod<T>
|
||||
|
||||
type PropMethod<T, TConstructor = any> = [T] extends [
|
||||
((...args: any) => any) | undefined,
|
||||
] // if is function with args, allowing non-required functions
|
||||
? { new (): TConstructor; (): T; readonly prototype: TConstructor } // Create Function like constructor
|
||||
: never
|
||||
// Function-like constructor so that `Function as PropType<() => void>`
|
||||
// type-checks. Intentionally not conditional on `T`: a conditional type here
|
||||
// makes `PropType<T>` a deferred (generic) type whenever `T` is a type
|
||||
// parameter, which in turn defers `RequiredKeys` / `OptionalKeys` and drops
|
||||
// every non-required prop from `ExtractPropTypes` (#9546).
|
||||
type PropMethod<T, TConstructor = any> = {
|
||||
new (): TConstructor
|
||||
(): T
|
||||
readonly prototype: TConstructor
|
||||
}
|
||||
|
||||
type RequiredKeys<T> = {
|
||||
[K in keyof T]: T[K] extends
|
||||
@@ -124,7 +129,10 @@ type InferPropType<T, NullAsAny = true> = [T] extends [null]
|
||||
: [T] extends [Prop<infer V, infer D>]
|
||||
? unknown extends V
|
||||
? keyof V extends never
|
||||
? IfAny<V, V, D>
|
||||
? // `D` is only meaningful when a default is present; falling
|
||||
// back to `V` keeps the deferred type assignable to `V`
|
||||
// when `V` is a type parameter (#9546)
|
||||
IfAny<V, V, unknown extends D ? V : D>
|
||||
: V
|
||||
: V
|
||||
: T
|
||||
|
||||
Reference in New Issue
Block a user