Skip to content

Commit e33291b

Browse files
authored
types: improve UnwrapRef (#579)
2 parents 1068212 + 8a74260 commit e33291b

File tree

3 files changed

+53
-16
lines changed

3 files changed

+53
-16
lines changed

packages/reactivity/src/ref.ts

+41-13
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,44 @@ export function toRef<T extends object, K extends keyof T>(
128128
// RelativePath extends object -> true
129129
type BaseTypes = string | number | boolean | Node | Window
130130

131-
// Recursively unwraps nested value bindings.
132-
export type UnwrapRef<T> = {
133-
cRef: T extends ComputedRef<infer V> ? UnwrapRef<V> : T
134-
ref: T extends Ref<infer V> ? UnwrapRef<V> : T
135-
array: T
136-
object: { [K in keyof T]: UnwrapRef<T[K]> }
137-
}[T extends ComputedRef<any>
138-
? 'cRef'
139-
: T extends Array<any>
140-
? 'array'
141-
: T extends Ref | Function | CollectionTypes | BaseTypes
142-
? 'ref' // bail out on types that shouldn't be unwrapped
143-
: T extends object ? 'object' : 'ref']
131+
export type UnwrapRef<T> = T extends ComputedRef<infer V>
132+
? UnwrapRefSimple<V>
133+
: T extends Ref<infer V> ? UnwrapRefSimple<V> : UnwrapRefSimple<T>
134+
135+
type UnwrapRefSimple<T> = T extends Function | CollectionTypes | BaseTypes | Ref
136+
? T
137+
: T extends Array<any> ? T : T extends object ? UnwrappedObject<T> : T
138+
139+
// Extract all known symbols from an object
140+
// when unwrapping Object the symbols are not `in keyof`, this should cover all the
141+
// known symbols
142+
type SymbolExtract<T> = (T extends { [Symbol.asyncIterator]: infer V }
143+
? { [Symbol.asyncIterator]: V }
144+
: {}) &
145+
(T extends { [Symbol.hasInstance]: infer V }
146+
? { [Symbol.hasInstance]: V }
147+
: {}) &
148+
(T extends { [Symbol.isConcatSpreadable]: infer V }
149+
? { [Symbol.isConcatSpreadable]: V }
150+
: {}) &
151+
(T extends { [Symbol.iterator]: infer V } ? { [Symbol.iterator]: V } : {}) &
152+
(T extends { [Symbol.match]: infer V } ? { [Symbol.match]: V } : {}) &
153+
(T extends { [Symbol.matchAll]: infer V } ? { [Symbol.matchAll]: V } : {}) &
154+
(T extends { [Symbol.observable]: infer V }
155+
? { [Symbol.observable]: V }
156+
: {}) &
157+
(T extends { [Symbol.replace]: infer V } ? { [Symbol.replace]: V } : {}) &
158+
(T extends { [Symbol.search]: infer V } ? { [Symbol.search]: V } : {}) &
159+
(T extends { [Symbol.species]: infer V } ? { [Symbol.species]: V } : {}) &
160+
(T extends { [Symbol.split]: infer V } ? { [Symbol.split]: V } : {}) &
161+
(T extends { [Symbol.toPrimitive]: infer V }
162+
? { [Symbol.toPrimitive]: V }
163+
: {}) &
164+
(T extends { [Symbol.toStringTag]: infer V }
165+
? { [Symbol.toStringTag]: V }
166+
: {}) &
167+
(T extends { [Symbol.unscopables]: infer V }
168+
? { [Symbol.unscopables]: V }
169+
: {})
170+
171+
type UnwrappedObject<T> = { [P in keyof T]: UnwrapRef<T[P]> } & SymbolExtract<T>

packages/runtime-core/__tests__/apiTemplateRef.spec.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
h,
55
render,
66
nextTick,
7-
Ref,
87
defineComponent,
98
reactive
109
} from '@vue/runtime-test'
@@ -143,7 +142,7 @@ describe('api: template refs', () => {
143142
foo: ref(null),
144143
bar: ref(null)
145144
}
146-
const refKey = ref('foo') as Ref<keyof typeof refs>
145+
const refKey = ref<keyof typeof refs>('foo')
147146

148147
const Comp = {
149148
setup() {

test-dts/ref.test-d.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expectType } from 'tsd'
2-
import { Ref, ref, isRef, unref } from './index'
2+
import { Ref, ref, isRef, unref, UnwrapRef } from './index'
33

44
function plainType(arg: number | Ref<number>) {
55
// ref coercing
@@ -20,6 +20,16 @@ function plainType(arg: number | Ref<number>) {
2020
})
2121
expectType<Ref<{ foo: number }>>(nestedRef)
2222
expectType<{ foo: number }>(nestedRef.value)
23+
24+
// tuple
25+
expectType<[number, string]>(unref(ref([1, '1'])))
26+
27+
interface IteratorFoo {
28+
[Symbol.iterator]: any
29+
}
30+
31+
// with symbol
32+
expectType<IteratorFoo | null>(unref(ref<IteratorFoo | null>(null)))
2333
}
2434

2535
plainType(1)

0 commit comments

Comments
 (0)