Skip to content

types: changing UnwrapRef to a simpler implementation #579

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 10 commits into from
Apr 15, 2020
54 changes: 41 additions & 13 deletions packages/reactivity/src/ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,44 @@ export function toRef<T extends object, K extends keyof T>(
// RelativePath extends object -> true
type BaseTypes = string | number | boolean | Node | Window

// Recursively unwraps nested value bindings.
export type UnwrapRef<T> = {
cRef: T extends ComputedRef<infer V> ? UnwrapRef<V> : T
ref: T extends Ref<infer V> ? UnwrapRef<V> : T
array: T
object: { [K in keyof T]: UnwrapRef<T[K]> }
}[T extends ComputedRef<any>
? 'cRef'
: T extends Array<any>
? 'array'
: T extends Ref | Function | CollectionTypes | BaseTypes
? 'ref' // bail out on types that shouldn't be unwrapped
: T extends object ? 'object' : 'ref']
export type UnwrapRef<T> = T extends ComputedRef<infer V>
? UnwrapRefSimple<V>
: T extends Ref<infer V> ? UnwrapRefSimple<V> : UnwrapRefSimple<T>

type UnwrapRefSimple<T> = T extends Function | CollectionTypes | BaseTypes | Ref
? T
: T extends Array<any> ? T : T extends object ? UnwrappedObject<T> : T

// Extract all known symbols from an object
// when unwrapping Object the symbols are not `in keyof`, this should cover all the
// known symbols
type SymbolExtract<T> = (T extends { [Symbol.asyncIterator]: infer V }
? { [Symbol.asyncIterator]: V }
: {}) &
(T extends { [Symbol.hasInstance]: infer V }
? { [Symbol.hasInstance]: V }
: {}) &
(T extends { [Symbol.isConcatSpreadable]: infer V }
? { [Symbol.isConcatSpreadable]: V }
: {}) &
(T extends { [Symbol.iterator]: infer V } ? { [Symbol.iterator]: V } : {}) &
(T extends { [Symbol.match]: infer V } ? { [Symbol.match]: V } : {}) &
(T extends { [Symbol.matchAll]: infer V } ? { [Symbol.matchAll]: V } : {}) &
(T extends { [Symbol.observable]: infer V }
? { [Symbol.observable]: V }
: {}) &
(T extends { [Symbol.replace]: infer V } ? { [Symbol.replace]: V } : {}) &
(T extends { [Symbol.search]: infer V } ? { [Symbol.search]: V } : {}) &
(T extends { [Symbol.species]: infer V } ? { [Symbol.species]: V } : {}) &
(T extends { [Symbol.split]: infer V } ? { [Symbol.split]: V } : {}) &
(T extends { [Symbol.toPrimitive]: infer V }
? { [Symbol.toPrimitive]: V }
: {}) &
(T extends { [Symbol.toStringTag]: infer V }
? { [Symbol.toStringTag]: V }
: {}) &
(T extends { [Symbol.unscopables]: infer V }
? { [Symbol.unscopables]: V }
: {})

type UnwrappedObject<T> = { [P in keyof T]: UnwrapRef<T[P]> } & SymbolExtract<T>
3 changes: 1 addition & 2 deletions packages/runtime-core/__tests__/apiTemplateRef.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
h,
render,
nextTick,
Ref,
defineComponent,
reactive
} from '@vue/runtime-test'
Expand Down Expand Up @@ -143,7 +142,7 @@ describe('api: template refs', () => {
foo: ref(null),
bar: ref(null)
}
const refKey = ref('foo') as Ref<keyof typeof refs>
const refKey = ref<keyof typeof refs>('foo')

const Comp = {
setup() {
Expand Down
12 changes: 11 additions & 1 deletion test-dts/ref.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expectType } from 'tsd'
import { Ref, ref, isRef, unref } from './index'
import { Ref, ref, isRef, unref, UnwrapRef } from './index'

function plainType(arg: number | Ref<number>) {
// ref coercing
Expand All @@ -20,6 +20,16 @@ function plainType(arg: number | Ref<number>) {
})
expectType<Ref<{ foo: number }>>(nestedRef)
expectType<{ foo: number }>(nestedRef.value)

// tuple
expectType<[number, string]>(unref(ref([1, '1'])))

interface IteratorFoo {
[Symbol.iterator]: any
}

// with symbol
expectType<IteratorFoo | null>(unref(ref<IteratorFoo | null>(null)))
}

plainType(1)
Expand Down