fix(types): keep optional props when a runtime prop uses a generic PropType (#15523)

close #9546
This commit is contained in:
Önderhan İŞLEKOĞLU
2026-09-17 10:59:41 +08:00
committed by GitHub
parent 718f782b14
commit 243aabccf0
2 changed files with 48 additions and 6 deletions
@@ -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')
+14 -6
View File
@@ -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