]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(types): support generic usage with withDefaults + defineProps (#8335)
authorCarlos Rodrigues <david-181@hotmail.com>
Wed, 17 May 2023 01:25:48 +0000 (02:25 +0100)
committerGitHub <noreply@github.com>
Wed, 17 May 2023 01:25:48 +0000 (09:25 +0800)
fix #8310
fix #8331
fix #8325

packages/dts-test/setupHelpers.test-d.ts
packages/runtime-core/src/apiSetupHelpers.ts

index 9b68b345268b11a434b72c94f26ee809e1e0b8ec..77342590dc6a53fb13d13d88697658cac1841fa8 100644 (file)
@@ -100,6 +100,40 @@ describe('defineProps w/ union type declaration + withDefaults', () => {
   )
 })
 
+describe('defineProps w/ generic type declaration + withDefaults', <T extends number, TA extends {
+  a: string
+}, TString extends string>() => {
+  const res = withDefaults(
+    defineProps<{
+      n?: number
+      bool?: boolean
+
+      generic1?: T[] | { x: T }
+      generic2?: { x: T }
+      generic3?: TString
+      generic4?: TA
+    }>(),
+    {
+      n: 123,
+
+      generic1: () => [123, 33] as T[],
+      generic2: () => ({ x: 123 } as { x: T }),
+
+      generic3: () => 'test' as TString,
+      generic4: () => ({ a: 'test' } as TA)
+    }
+  )
+
+  res.n + 1
+
+  expectType<T[] | { x: T }>(res.generic1)
+  expectType<{ x: T }>(res.generic2)
+  expectType<TString>(res.generic3)
+  expectType<TA>(res.generic4)
+
+  expectType<boolean>(res.bool)
+})
+
 describe('defineProps w/ runtime declaration', () => {
   // runtime declaration
   const props = defineProps({
index 3ad330a1c27ed2b18c753bfe29871a433aa86335..76ad08cf63ac0996cc3f30f99931971ffe575907 100644 (file)
@@ -81,7 +81,10 @@ export function defineProps<
   PP extends ComponentObjectPropsOptions = ComponentObjectPropsOptions
 >(props: PP): Prettify<Readonly<ExtractPropTypes<PP>>>
 // overload 3: typed-based declaration
-export function defineProps<TypeProps>(): DefineProps<TypeProps>
+export function defineProps<TypeProps>(): DefineProps<
+  TypeProps,
+  BooleanKey<TypeProps>
+>
 // implementation
 export function defineProps() {
   if (__DEV__) {
@@ -90,8 +93,8 @@ export function defineProps() {
   return null as any
 }
 
-type DefineProps<T> = Readonly<T> & {
-  readonly [K in BooleanKey<T>]-?: boolean
+type DefineProps<T, BKeys extends keyof T> = Readonly<T> & {
+  readonly [K in BKeys]-?: boolean
 }
 
 type BooleanKey<T, K extends keyof T = keyof T> = K extends any
@@ -281,26 +284,27 @@ interface DefineModelOptions {
 type NotUndefined<T> = T extends undefined ? never : T
 
 type InferDefaults<T> = {
-  [K in keyof T]?: InferDefault<T, NotUndefined<T[K]>>
+  [K in keyof T]?: InferDefault<T, T[K]>
 }
 
-type InferDefault<P, T> = T extends
-  | null
-  | number
-  | string
-  | boolean
-  | symbol
-  | Function
-  ? T | ((props: P) => T)
-  : (props: P) => T
-
-type PropsWithDefaults<Base, Defaults> = Base & {
-  [K in keyof Defaults]: K extends keyof Base
+type NativeType = null | number | string | boolean | symbol | Function
+
+type InferDefault<P, T> =
+  | ((props: P) => T & {})
+  | (T extends NativeType ? T : never)
+
+type PropsWithDefaults<
+  T,
+  Defaults extends InferDefaults<T>,
+  BKeys extends keyof T
+> = Omit<T, keyof Defaults> & {
+  [K in keyof Defaults]-?: K extends keyof T
     ? Defaults[K] extends undefined
-      ? Base[K]
-      : NotUndefined<Base[K]>
+      ? T[K]
+      : NotUndefined<T[K]>
     : never
-}
+} & { readonly [K in BKeys]-?: boolean }
+
 /**
  * Vue `<script setup>` compiler macro for providing props default values when
  * using type-based `defineProps` declaration.
@@ -321,10 +325,14 @@ type PropsWithDefaults<Base, Defaults> = Base & {
  *
  * @see {@link https://vuejs.org/guide/typescript/composition-api.html#typing-component-props}
  */
-export function withDefaults<Props, Defaults extends InferDefaults<Props>>(
-  props: Props,
+export function withDefaults<
+  T,
+  BKeys extends keyof T,
+  Defaults extends InferDefaults<T>
+>(
+  props: DefineProps<T, BKeys>,
   defaults: Defaults
-): PropsWithDefaults<Props, Defaults> {
+): PropsWithDefaults<T, Defaults, BKeys> {
   if (__DEV__) {
     warnRuntimeUsage(`withDefaults`)
   }