Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

types(reactivity): fix the return type error when the toRaw parameter type is readonly #9124

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
21 changes: 21 additions & 0 deletions packages-private/dts-test/reactivity.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
readonly,
ref,
shallowReadonly,
toRaw,
} from 'vue'
import { describe, expectType } from './utils'

Expand Down Expand Up @@ -130,3 +131,23 @@ describe('should not error when assignment', () => {
record2 = arr
expectType<string>(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<Foo>(rawObj)

// shallowReadonly
const shallowR = shallowReadonly(foo)
const shallowRawObj = toRaw(shallowR)
expectType<Foo>(shallowRawObj)
})
19 changes: 17 additions & 2 deletions packages/reactivity/src/reactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
/**
* Original type used to hold readonly function parameters
*/
[ReadonlyRawSymbol]: T
}

export interface Target {
[ReactiveFlags.SKIP]?: boolean
[ReactiveFlags.IS_REACTIVE]?: boolean
Expand Down Expand Up @@ -204,7 +213,7 @@ export type DeepReadonly<T> = T extends Builtin
*/
export function readonly<T extends object>(
target: T,
): DeepReadonly<UnwrapNestedRefs<T>> {
): DeepReadonly<UnwrapNestedRefs<T>> & ReadonlyRaw<T> {
return createReactiveObject(
target,
true,
Expand Down Expand Up @@ -244,7 +253,9 @@ export function readonly<T extends object>(
* @param target - The source object.
* @see {@link https://vuejs.org/api/reactivity-advanced.html#shallowreadonly}
*/
export function shallowReadonly<T extends object>(target: T): Readonly<T> {
export function shallowReadonly<T extends object>(
target: T,
): Readonly<T> & ReadonlyRaw<T> {
return createReactiveObject(
target,
true,
Expand Down Expand Up @@ -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<T extends ReadonlyRaw<any>>(
observed: T,
): T[typeof ReadonlyRawSymbol]
export function toRaw<T>(observed: T): T
export function toRaw<T>(observed: T): T {
const raw = observed && (observed as Target)[ReactiveFlags.RAW]
return raw ? toRaw(raw) : observed
Expand Down