diff --git a/packages-private/dts-test/reactivity.test-d.ts b/packages-private/dts-test/reactivity.test-d.ts index f5b9176729c..eeded3a7be6 100644 --- a/packages-private/dts-test/reactivity.test-d.ts +++ b/packages-private/dts-test/reactivity.test-d.ts @@ -5,6 +5,7 @@ import { readonly, ref, shallowReadonly, + toRaw, } from 'vue' import { describe, expectType } from './utils' @@ -130,3 +131,23 @@ describe('should not error when assignment', () => { record2 = arr expectType(record2[0]) }) + +// #7478 +describe('readonly raw type', () => { + type Foo = { a: number; b: string; c: { d: number } } + const foo: Foo = { + a: 1, + b: 'b', + c: { d: 2 }, + } + + // readonly + const r = readonly(foo) + const rawObj = toRaw(r) + expectType(rawObj) + + // shallowReadonly + const shallowR = shallowReadonly(foo) + const shallowRawObj = toRaw(shallowR) + expectType(shallowRawObj) +}) diff --git a/packages/reactivity/src/reactive.ts b/packages/reactivity/src/reactive.ts index 729c854965e..a41489790b5 100644 --- a/packages/reactivity/src/reactive.ts +++ b/packages/reactivity/src/reactive.ts @@ -15,6 +15,15 @@ import type { RawSymbol, Ref, UnwrapRefSimple } from './ref' import { ReactiveFlags } from './constants' import { warn } from './warning' +declare const ReadonlyRawSymbol: unique symbol + +interface ReadonlyRaw { + /** + * Original type used to hold readonly function parameters + */ + [ReadonlyRawSymbol]: T +} + export interface Target { [ReactiveFlags.SKIP]?: boolean [ReactiveFlags.IS_REACTIVE]?: boolean @@ -204,7 +213,7 @@ export type DeepReadonly = T extends Builtin */ export function readonly( target: T, -): DeepReadonly> { +): DeepReadonly> & ReadonlyRaw { return createReactiveObject( target, true, @@ -244,7 +253,9 @@ export function readonly( * @param target - The source object. * @see {@link https://vuejs.org/api/reactivity-advanced.html#shallowreadonly} */ -export function shallowReadonly(target: T): Readonly { +export function shallowReadonly( + target: T, +): Readonly & ReadonlyRaw { return createReactiveObject( target, true, @@ -375,6 +386,10 @@ export function isProxy(value: any): boolean { * @param observed - The object for which the "raw" value is requested. * @see {@link https://vuejs.org/api/reactivity-advanced.html#toraw} */ +export function toRaw>( + observed: T, +): T[typeof ReadonlyRawSymbol] +export function toRaw(observed: T): T export function toRaw(observed: T): T { const raw = observed && (observed as Target)[ReactiveFlags.RAW] return raw ? toRaw(raw) : observed