types: improve UnwrapRef (#579)

This commit is contained in:
Evan You 2020-04-15 11:26:41 -04:00 committed by GitHub
commit e33291bd0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 16 deletions

View File

@ -128,16 +128,44 @@ export function toRef<T extends object, K extends keyof T>(
// RelativePath extends object -> true // RelativePath extends object -> true
type BaseTypes = string | number | boolean | Node | Window type BaseTypes = string | number | boolean | Node | Window
// Recursively unwraps nested value bindings. export type UnwrapRef<T> = T extends ComputedRef<infer V>
export type UnwrapRef<T> = { ? UnwrapRefSimple<V>
cRef: T extends ComputedRef<infer V> ? UnwrapRef<V> : T : T extends Ref<infer V> ? UnwrapRefSimple<V> : UnwrapRefSimple<T>
ref: T extends Ref<infer V> ? UnwrapRef<V> : T
array: T type UnwrapRefSimple<T> = T extends Function | CollectionTypes | BaseTypes | Ref
object: { [K in keyof T]: UnwrapRef<T[K]> } ? T
}[T extends ComputedRef<any> : T extends Array<any> ? T : T extends object ? UnwrappedObject<T> : T
? 'cRef'
: T extends Array<any> // Extract all known symbols from an object
? 'array' // when unwrapping Object the symbols are not `in keyof`, this should cover all the
: T extends Ref | Function | CollectionTypes | BaseTypes // known symbols
? 'ref' // bail out on types that shouldn't be unwrapped type SymbolExtract<T> = (T extends { [Symbol.asyncIterator]: infer V }
: T extends object ? 'object' : 'ref'] ? { [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>

View File

@ -4,7 +4,6 @@ import {
h, h,
render, render,
nextTick, nextTick,
Ref,
defineComponent, defineComponent,
reactive reactive
} from '@vue/runtime-test' } from '@vue/runtime-test'
@ -143,7 +142,7 @@ describe('api: template refs', () => {
foo: ref(null), foo: ref(null),
bar: ref(null) bar: ref(null)
} }
const refKey = ref('foo') as Ref<keyof typeof refs> const refKey = ref<keyof typeof refs>('foo')
const Comp = { const Comp = {
setup() { setup() {

View File

@ -1,5 +1,5 @@
import { expectType } from 'tsd' 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>) { function plainType(arg: number | Ref<number>) {
// ref coercing // ref coercing
@ -20,6 +20,16 @@ function plainType(arg: number | Ref<number>) {
}) })
expectType<Ref<{ foo: number }>>(nestedRef) expectType<Ref<{ foo: number }>>(nestedRef)
expectType<{ foo: number }>(nestedRef.value) 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) plainType(1)