fix(types): fix ref unwrapping type inference for nested shallowReactive & shallowRef

fix #4771
This commit is contained in:
Evan You 2021-10-09 17:51:09 -04:00
parent e772108922
commit 20a361541c
5 changed files with 88 additions and 15 deletions

View File

@ -27,6 +27,7 @@ export {
toRaw, toRaw,
ReactiveFlags, ReactiveFlags,
DeepReadonly, DeepReadonly,
ShallowReactive,
UnwrapNestedRefs UnwrapNestedRefs
} from './reactive' } from './reactive'
export { export {

View File

@ -99,12 +99,18 @@ export function reactive(target: object) {
) )
} }
export declare const ShallowReactiveMarker: unique symbol
export type ShallowReactive<T> = T & { [ShallowReactiveMarker]?: true }
/** /**
* Return a shallowly-reactive copy of the original object, where only the root * Return a shallowly-reactive copy of the original object, where only the root
* level properties are reactive. It also does not auto-unwrap refs (even at the * level properties are reactive. It also does not auto-unwrap refs (even at the
* root level). * root level).
*/ */
export function shallowReactive<T extends object>(target: T): T { export function shallowReactive<T extends object>(
target: T
): ShallowReactive<T> {
return createReactiveObject( return createReactiveObject(
target, target,
false, false,

View File

@ -1,7 +1,13 @@
import { isTracking, trackEffects, triggerEffects } from './effect' import { isTracking, trackEffects, triggerEffects } from './effect'
import { TrackOpTypes, TriggerOpTypes } from './operations' import { TrackOpTypes, TriggerOpTypes } from './operations'
import { isArray, hasChanged } from '@vue/shared' import { isArray, hasChanged } from '@vue/shared'
import { isProxy, toRaw, isReactive, toReactive } from './reactive' import {
isProxy,
toRaw,
isReactive,
toReactive,
ShallowReactiveMarker
} from './reactive'
import { CollectionTypes } from './collectionHandlers' import { CollectionTypes } from './collectionHandlers'
import { createDep, Dep } from './dep' import { createDep, Dep } from './dep'
@ -74,11 +80,15 @@ export function ref(value?: unknown) {
return createRef(value, false) return createRef(value, false)
} }
declare const ShallowRefMarker: unique symbol
type ShallowRef<T = any> = Ref<T> & { [ShallowRefMarker]?: true }
export function shallowRef<T extends object>( export function shallowRef<T extends object>(
value: T value: T
): T extends Ref ? T : Ref<T> ): T extends Ref ? T : ShallowRef<T>
export function shallowRef<T>(value: T): Ref<T> export function shallowRef<T>(value: T): ShallowRef<T>
export function shallowRef<T = any>(): Ref<T | undefined> export function shallowRef<T = any>(): ShallowRef<T | undefined>
export function shallowRef(value?: unknown) { export function shallowRef(value?: unknown) {
return createRef(value, true) return createRef(value, true)
} }
@ -215,6 +225,7 @@ class ObjectRefImpl<T extends object, K extends keyof T> {
} }
export type ToRef<T> = [T] extends [Ref] ? T : Ref<T> export type ToRef<T> = [T] extends [Ref] ? T : Ref<T>
export function toRef<T extends object, K extends keyof T>( export function toRef<T extends object, K extends keyof T>(
object: T, object: T,
key: K key: K
@ -258,7 +269,9 @@ export type ShallowUnwrapRef<T> = {
: T[K] : T[K]
} }
export type UnwrapRef<T> = T extends Ref<infer V> export type UnwrapRef<T> = T extends ShallowRef<infer V>
? V
: T extends Ref<infer V>
? UnwrapRefSimple<V> ? UnwrapRefSimple<V>
: UnwrapRefSimple<T> : UnwrapRefSimple<T>
@ -271,7 +284,7 @@ export type UnwrapRefSimple<T> = T extends
? T ? T
: T extends Array<any> : T extends Array<any>
? { [K in keyof T]: UnwrapRefSimple<T[K]> } ? { [K in keyof T]: UnwrapRefSimple<T[K]> }
: T extends object : T extends object & { [ShallowReactiveMarker]?: never }
? { ? {
[P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]> [P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>
} }

View File

@ -161,7 +161,8 @@ export {
UnwrapRef, UnwrapRef,
ShallowUnwrapRef, ShallowUnwrapRef,
WritableComputedOptions, WritableComputedOptions,
DeepReadonly DeepReadonly,
ShallowReactive
} from '@vue/reactivity' } from '@vue/reactivity'
export { export {
WatchEffect, WatchEffect,

View File

@ -239,13 +239,65 @@ function testUnrefGenerics<T>(p: T | Ref<T>) {
testUnrefGenerics(1) testUnrefGenerics(1)
// #4732 // #4732
const baz = shallowReactive({ describe('ref in shallow reactive', () => {
foo: { const baz = shallowReactive({
bar: ref(42) foo: {
} bar: ref(42)
}
})
const foo = toRef(baz, 'foo')
expectType<Ref<number>>(foo.value.bar)
expectType<number>(foo.value.bar.value)
}) })
const foo = toRef(baz, 'foo') // #4771
describe('shallow reactive in reactive', () => {
const baz = reactive({
foo: shallowReactive({
a: {
b: ref(42)
}
})
})
expectType<Ref<number>>(foo.value.bar) const foo = toRef(baz, 'foo')
expectType<number>(foo.value.bar.value)
expectType<Ref<number>>(foo.value.a.b)
expectType<number>(foo.value.a.b.value)
})
describe('shallow ref in reactive', () => {
const x = reactive({
foo: shallowRef({
bar: {
baz: ref(123),
qux: reactive({
z: ref(123)
})
}
})
})
expectType<Ref<number>>(x.foo.bar.baz)
expectType<number>(x.foo.bar.qux.z)
})
describe('ref in shallow ref', () => {
const x = shallowRef({
a: ref(123)
})
expectType<Ref<number>>(x.value.a)
})
describe('reactive in shallow ref', () => {
const x = shallowRef({
a: reactive({
b: ref(0)
})
})
expectType<number>(x.value.a.b)
})