瀏覽代碼

types: fix withDefaults type

三咲智子 Kevin Deng 2 年之前
父節點
當前提交
f703a11a43
共有 2 個文件被更改,包括 47 次插入22 次删除
  1. 27 10
      packages/dts-test/setupHelpers.test-d.ts
  2. 20 12
      packages/runtime-core/src/apiSetupHelpers.ts

+ 27 - 10
packages/dts-test/setupHelpers.test-d.ts

@@ -19,7 +19,7 @@ describe('defineProps w/ type declaration', () => {
   const props = defineProps<{
     foo: string
     bool?: boolean
-    boolAndUndefined: boolean | undefined
+    boolOrUndefined: boolean | undefined
   }>()
   // explicitly declared type should be refined
   expectType<string>(props.foo)
@@ -27,7 +27,7 @@ describe('defineProps w/ type declaration', () => {
   props.bar
 
   expectType<boolean>(props.bool)
-  expectType<boolean>(props.boolAndUndefined)
+  expectType<boolean>(props.boolOrUndefined)
 })
 
 describe('defineProps w/ generics', () => {
@@ -52,7 +52,8 @@ describe('defineProps w/ type declaration + withDefaults', () => {
       y?: string
       z?: string
       bool?: boolean
-      boolAndUndefined: boolean | undefined
+      boolOrUndefined: boolean | undefined
+      defaultUndefined?: string
     }>(),
     {
       number: 123,
@@ -61,10 +62,14 @@ describe('defineProps w/ type declaration + withDefaults', () => {
       fn: () => {},
       genStr: () => '',
       y: undefined,
-      z: 'string'
+      z: 'string',
+      defaultUndefined: undefined as string | undefined
     }
   )
 
+  // @ts-expect-error
+  res.number++
+
   res.number + 1
   res.arr.push('hi')
   res.obj.x
@@ -75,12 +80,21 @@ describe('defineProps w/ type declaration + withDefaults', () => {
   // @ts-expect-error
   res.y.slice()
 
-  expectType<string | undefined>(res.x)
-  expectType<string | undefined>(res.y)
-  expectType<string>(res.z)
-
-  expectType<boolean>(res.bool)
-  expectType<boolean>(res.boolAndUndefined)
+  type T = {
+    number: number
+    arr: string[]
+    obj: { x: number }
+    fn: (e: string) => void
+    genStr: string
+    x: string | undefined
+    y: string | undefined
+    z: string
+    bool: boolean
+    boolOrUndefined: boolean
+    defaultUndefined: string | undefined
+  }
+  expectType<T>(res)
+  expectType<typeof res>({} as T)
 })
 
 describe('defineProps w/ union type declaration + withDefaults', () => {
@@ -127,6 +141,9 @@ describe('defineProps w/ generic type declaration + withDefaults', <T extends
 
   res.n + 1
 
+  // @ts-expect-error readonly
+  res.n++
+
   expectType<T[] | { x: T }>(res.generic1)
   expectType<{ x: T }>(res.generic2)
   expectType<TString>(res.generic3)

+ 20 - 12
packages/runtime-core/src/apiSetupHelpers.ts

@@ -293,23 +293,31 @@ type InferDefault<P, T> =
   | ((props: P) => T & {})
   | (T extends NativeType ? T : never)
 
+type NonPartial<T> = {
+  [K in keyof Required<T>]: T[K]
+}
+
+type UndefinedDefault<T, Default> = Default extends undefined
+  ? T
+  : NotUndefined<T>
+
 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
-      ? T[K]
-      : NotUndefined<T[K]>
-    : never
-} & {
-  readonly [K in BKeys]-?: K extends keyof Defaults
-    ? Defaults[K] extends undefined
-      ? boolean | undefined
+> = Readonly<
+  NonPartial<Omit<T, keyof (Defaults | BKeys)>> & {
+    [K in keyof Defaults]-?: K extends keyof T
+      ? UndefinedDefault<T[K], Defaults[K]>
+      : never
+  } & {
+    [K in BKeys]: K extends keyof Defaults
+      ? Defaults[K] extends undefined
+        ? boolean | undefined
+        : boolean
       : boolean
-    : boolean
-}
+  }
+>
 
 /**
  * Vue `<script setup>` compiler macro for providing props default values when