diff --git a/src/v3/reactivity/ref.ts b/src/v3/reactivity/ref.ts
index 33495806da1..9a1d441adc1 100644
--- a/src/v3/reactivity/ref.ts
+++ b/src/v3/reactivity/ref.ts
@@ -51,9 +51,13 @@ declare const ShallowRefMarker: unique symbol
 
 export type ShallowRef<T = any> = Ref<T> & { [ShallowRefMarker]?: true }
 
-export function shallowRef<T>(value: T | Ref<T>): Ref<T> | ShallowRef<T>
-export function shallowRef<T extends Ref>(value: T): T
-export function shallowRef<T>(value: T): ShallowRef<T>
+export function shallowRef<T>(
+  value: T
+): Ref extends T
+  ? T extends Ref
+    ? IfAny<T, ShallowRef<T>, T>
+    : ShallowRef<T>
+  : ShallowRef<T>
 export function shallowRef<T = any>(): ShallowRef<T | undefined>
 export function shallowRef(value?: unknown) {
   return createRef(value, true)
diff --git a/types/test/v3/reactivity-test.ts b/types/test/v3/reactivity-test.ts
index c357bf8d5c7..7ce3b9db62c 100644
--- a/types/test/v3/reactivity-test.ts
+++ b/types/test/v3/reactivity-test.ts
@@ -13,9 +13,10 @@ import {
   markRaw,
   shallowReadonly,
   set,
-  del
+  del,
+  ShallowRef
 } from '../../index'
-import { IsUnion, describe, expectType } from '../utils'
+import { IsUnion, describe, expectType, expectError } from '../utils'
 
 function plainType(arg: number | Ref<number>) {
   // ref coercing
@@ -163,6 +164,15 @@ if (shallowStatus.value === 'initial') {
   shallowStatus.value = 'invalidating'
 }
 
+{
+  // should return ShallowRef<T> | Ref<T>, not ShallowRef<T | Ref<T>>
+  expectType<ShallowRef<{ name: string }> | Ref<{ name: string }>>(
+    shallowRef({} as { name: string } | Ref<{ name: string }>)
+  )
+  expectType<ShallowRef<number> | Ref<string[]> | ShallowRef<string>>(
+    shallowRef('' as Ref<string[]> | string | number)
+  )
+}
 const refStatus = ref<Status>('initial')
 if (refStatus.value === 'initial') {
   expectType<Ref<Status>>(shallowStatus)
@@ -386,7 +396,6 @@ describe('set/del', () => {
   del([], 'fse', 123)
 })
 
-
 {
   //#12978
   type Steps = { step: '1' } | { step: '2' }
@@ -395,4 +404,4 @@ describe('set/del', () => {
 
   expectType<IsUnion<typeof shallowUnionGenParam>>(false)
   expectType<IsUnion<typeof shallowUnionAsCast>>(false)
-}
\ No newline at end of file
+}