Parcourir la source

feat(types/reactivity): use `DeepReadonly` type for `readonly` return type (#1462)

close #1452
Pick il y a 5 ans
Parent
commit
b772bba558
3 fichiers modifiés avec 33 ajouts et 2 suppressions
  1. 1 1
      .github/contributing.md
  2. 23 1
      packages/reactivity/src/reactive.ts
  3. 9 0
      test-dts/reactivity.test-d.ts

+ 1 - 1
.github/contributing.md

@@ -244,7 +244,7 @@ Test coverage is continuously deployed at https://vue-next-coverage.netlify.app/
 
 This project uses [tsd](https://github.com/SamVerschueren/tsd) to test the built definition files (`*.d.ts`).
 
-Type tests are located in the `test-dts` directory. To run the dts tests, run `yarn test-dts`. Note that the type test requires all relevant `*.d.ts` files to be built first (and the script does it for you). Once the `d.ts` files are built and up-to-date, the tests can be re-run by simply running `./node_modules/.bin/tsd`.
+Type tests are located in the `test-dts` directory. To run the dts tests, run `yarn test-dts`. Note that the type test requires all relevant `*.d.ts` files to be built first (and the script does it for you). Once the `d.ts` files are built and up-to-date, the tests can be re-run by simply running `yarn test-dts`.
 
 ## Financial Contribution
 

+ 23 - 1
packages/reactivity/src/reactive.ts

@@ -72,9 +72,31 @@ export function shallowReactive<T extends object>(target: T): T {
   )
 }
 
+type Primitive = string | number | boolean | bigint | symbol | undefined | null
+type Builtin = Primitive | Function | Date | Error | RegExp
+type DeepReadonly<T> = T extends Builtin
+  ? T
+  : T extends Map<infer K, infer V>
+    ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
+    : T extends ReadonlyMap<infer K, infer V>
+      ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
+      : T extends WeakMap<infer K, infer V>
+        ? WeakMap<DeepReadonly<K>, DeepReadonly<V>>
+        : T extends Set<infer U>
+          ? ReadonlySet<DeepReadonly<U>>
+          : T extends ReadonlySet<infer U>
+            ? ReadonlySet<DeepReadonly<U>>
+            : T extends WeakSet<infer U>
+              ? WeakSet<DeepReadonly<U>>
+              : T extends Promise<infer U>
+                ? Promise<DeepReadonly<U>>
+                : T extends {}
+                  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
+                  : Readonly<T>
+
 export function readonly<T extends object>(
   target: T
-): Readonly<UnwrapNestedRefs<T>> {
+): DeepReadonly<UnwrapNestedRefs<T>> {
   return createReactiveObject(
     target,
     true,

+ 9 - 0
test-dts/reactivity.test-d.ts

@@ -0,0 +1,9 @@
+import { readonly, describe, expectError } from './index'
+
+describe('should support DeepReadonly', () => {
+  const r = readonly({ obj: { k: 'v' } })
+  // @ts-expect-error
+  expectError((r.obj = {}))
+  // @ts-expect-error
+  expectError((r.obj.k = 'x'))
+})