Skip to content

feat(reactivity): improve support of getter usage in reactivity APIs #7997

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

Merged
merged 11 commits into from
Apr 2, 2023
Prev Previous commit
Next Next commit
feat(reactivity): further expand toRef support range
  • Loading branch information
yyx990803 committed Mar 31, 2023
commit 5cdf2ecf467362d483150a496260fcf58e553ffa
39 changes: 38 additions & 1 deletion packages/dts-test/ref.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {
toRefs,
ToRefs,
shallowReactive,
readonly
readonly,
MaybeRef,
MaybeReadonlyRef
} from 'vue'
import { expectType, describe } from './utils'

Expand Down Expand Up @@ -327,3 +329,38 @@ describe('reactive in shallow ref', () => {

expectType<number>(x.value.a.b)
})

describe('toRef <-> unref', () => {
function foo(
a: MaybeRef<string>,
b: () => string,
c: MaybeReadonlyRef<string>
) {
const r = toRef(a)
expectType<Ref<string>>(r)
// writable
r.value = 'foo'

const rb = toRef(b)
expectType<Readonly<Ref<string>>>(rb)
// @ts-expect-error ref created from getter shuld be readonly
rb.value = 'foo'

const rc = toRef(c)
expectType<Readonly<Ref<string> | Ref<string>>>(rc)
// @ts-expect-error ref created from MaybeReadonlyRef shuld be readonly
rc.value = 'foo'

return {
r: unref(r),
rb: unref(rb),
rc: unref(rc)
}
}

expectType<{
r: string
rb: string
rc: string
}>(foo('foo', () => 'bar', ref('baz')))
})
1 change: 1 addition & 0 deletions packages/reactivity/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export {
triggerRef,
type Ref,
type MaybeRef,
type MaybeReadonlyRef,
type ToRef,
type ToRefs,
type UnwrapRef,
Expand Down
45 changes: 30 additions & 15 deletions packages/reactivity/src/ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import {
triggerEffects
} from './effect'
import { TrackOpTypes, TriggerOpTypes } from './operations'
import { isArray, hasChanged, IfAny, isFunction } from '@vue/shared'
import {
isArray,
hasChanged,
IfAny,
isFunction,
isPlainObject
} from '@vue/shared'
import {
isProxy,
toRaw,
Expand Down Expand Up @@ -87,9 +93,6 @@ export function isRef(r: any): r is Ref {
* @param value - The object to wrap in the ref.
* @see {@link https://vuejs.org/api/reactivity-core.html#ref}
*/
export function ref<T extends object>(
value: T
): [T] extends [Ref] ? T : Ref<UnwrapRef<T>>
export function ref<T>(value: T): Ref<UnwrapRef<T>>
export function ref<T = any>(): Ref<T | undefined>
export function ref(value?: unknown) {
Expand Down Expand Up @@ -191,7 +194,8 @@ export function triggerRef(ref: Ref) {
triggerRefValue(ref, __DEV__ ? ref.value : void 0)
}

export type MaybeRef<T = any> = T | Ref<T> | (() => T)
export type MaybeRef<T = any> = T | Ref<T>
export type MaybeReadonlyRef<T = any> = MaybeRef<T> | (() => T)

/**
* Returns the inner value if the argument is a ref, otherwise return the
Expand All @@ -209,7 +213,7 @@ export type MaybeRef<T = any> = T | Ref<T> | (() => T)
* @param ref - Ref or plain value to be converted into the plain value.
* @see {@link https://vuejs.org/api/reactivity-utilities.html#unref}
*/
export function unref<T>(ref: MaybeRef<T>): T {
export function unref<T>(ref: MaybeReadonlyRef<T>): T {
return isRef(ref) ? (ref.value as any) : isFunction(ref) ? ref() : ref
}

Expand Down Expand Up @@ -372,9 +376,12 @@ export type ToRef<T> = IfAny<T, Ref<T>, [T] extends [Ref] ? T : Ref<T>>
* @param key - Name of the property in the reactive object.
* @see {@link https://vuejs.org/api/reactivity-utilities.html#toref}
*/
export function toRef<T extends () => any>(
getter: T
): T extends () => infer R ? Readonly<ShallowRef<R>> : never
// export function toRef<T extends () => any>(
// getter: T
// ): T extends () => infer R ? Readonly<Ref<R>> : never
export function toRef<T>(
value: T
): T extends () => infer R ? Readonly<Ref<R>> : Ref<UnwrapRef<T>>
export function toRef<T extends object, K extends keyof T>(
object: T,
key: K
Expand All @@ -385,17 +392,25 @@ export function toRef<T extends object, K extends keyof T>(
defaultValue: T[K]
): ToRef<Exclude<T[K], undefined>>
export function toRef(
objectOrGetter: Record<string, any> | (() => unknown),
source: Record<string, any> | MaybeRef,
key?: string,
defaultValue?: unknown
): Ref {
if (isFunction(objectOrGetter)) {
return new GetterRefImpl(objectOrGetter as () => unknown) as any
} else {
const val = objectOrGetter[key!]
if (isRef(source)) {
return source
} else if (isFunction(source)) {
return new GetterRefImpl(source as () => unknown) as any
} else if (isPlainObject(source) && key) {
const val = (source as Record<string, any>)[key]
return isRef(val)
? val
: (new ObjectRefImpl(objectOrGetter, key!, defaultValue) as any)
: (new ObjectRefImpl(
source as Record<string, any>,
key,
defaultValue
) as any)
} else {
return ref(source)
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/runtime-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ export { TrackOpTypes, TriggerOpTypes } from '@vue/reactivity'
export type {
Ref,
MaybeRef,
MaybeReadonlyRef,
ToRef,
ToRefs,
UnwrapRef,
Expand Down