| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585 |
- import {
- type ComputedRef,
- type MaybeRef,
- type MaybeRefOrGetter,
- type Ref,
- type ShallowRef,
- type TemplateRef,
- type ToRefs,
- type WritableComputedRef,
- computed,
- customRef,
- isRef,
- proxyRefs,
- reactive,
- readonly,
- ref,
- shallowReactive,
- shallowRef,
- toRef,
- toRefs,
- toValue,
- unref,
- useTemplateRef,
- } from 'vue'
- import { type IsAny, type IsUnion, describe, expectType } from './utils'
- function plainType(arg: number | Ref<number>) {
- // ref coercing
- const coerced = ref(arg)
- expectType<Ref<number>>(coerced)
- // isRef as type guard
- if (isRef(arg)) {
- expectType<Ref<number>>(arg)
- }
- // ref unwrapping
- expectType<number>(unref(arg))
- expectType<number>(toValue(arg))
- expectType<number>(toValue(() => 123))
- // ref inner type should be unwrapped
- const nestedRef = ref({
- foo: ref(1),
- })
- expectType<{ foo: number }>(nestedRef.value)
- // ref boolean
- const falseRef = ref(false)
- expectType<Ref<boolean>>(falseRef)
- expectType<boolean>(falseRef.value)
- // ref true
- const trueRef = ref<true>(true)
- expectType<Ref<true>>(trueRef)
- expectType<true>(trueRef.value)
- // tuple
- expectType<[number, string]>(unref(ref([1, '1'])))
- interface IteratorFoo {
- [Symbol.iterator]: any
- }
- // with symbol
- expectType<Ref<IteratorFoo | null | undefined>>(
- ref<IteratorFoo | null | undefined>(),
- )
- // should not unwrap ref inside arrays
- const arr = ref([1, new Map<string, any>(), ref('1')]).value
- const value = arr[0]
- if (isRef(value)) {
- expectType<Ref>(value)
- } else if (typeof value === 'number') {
- expectType<number>(value)
- } else {
- // should narrow down to Map type
- // and not contain any Ref type
- expectType<Map<string, any>>(value)
- }
- // should still unwrap in objects nested in arrays
- const arr2 = ref([{ a: ref(1) }]).value
- expectType<number>(arr2[0].a)
- // any value should return Ref<any>, not any
- const a = ref(1 as any)
- expectType<IsAny<typeof a>>(false)
- }
- plainType(1)
- function bailType(arg: HTMLElement | Ref<HTMLElement>) {
- // ref coercing
- const coerced = ref(arg)
- expectType<Ref<HTMLElement>>(coerced)
- // isRef as type guard
- if (isRef(arg)) {
- expectType<Ref<HTMLElement>>(arg)
- }
- // ref unwrapping
- expectType<HTMLElement>(unref(arg))
- // ref inner type should be unwrapped
- const nestedRef = ref({ foo: ref(document.createElement('DIV')) })
- expectType<Ref<{ foo: HTMLElement }>>(nestedRef)
- expectType<{ foo: HTMLElement }>(nestedRef.value)
- }
- const el = document.createElement('DIV')
- bailType(el)
- function withSymbol() {
- const customSymbol = Symbol()
- const obj = {
- [Symbol.asyncIterator]: ref(1),
- [Symbol.hasInstance]: { a: ref('a') },
- [Symbol.isConcatSpreadable]: { b: ref(true) },
- [Symbol.iterator]: [ref(1)],
- [Symbol.match]: new Set<Ref<number>>(),
- [Symbol.matchAll]: new Map<number, Ref<string>>(),
- [Symbol.replace]: { arr: [ref('a')] },
- [Symbol.search]: { set: new Set<Ref<number>>() },
- [Symbol.species]: { map: new Map<number, Ref<string>>() },
- [Symbol.split]: new WeakSet<Ref<boolean>>(),
- [Symbol.toPrimitive]: new WeakMap<Ref<boolean>, string>(),
- [Symbol.toStringTag]: { weakSet: new WeakSet<Ref<boolean>>() },
- [Symbol.unscopables]: { weakMap: new WeakMap<Ref<boolean>, string>() },
- [customSymbol]: { arr: [ref(1)] },
- }
- const objRef = ref(obj)
- expectType<Ref<number>>(objRef.value[Symbol.asyncIterator])
- expectType<{ a: Ref<string> }>(objRef.value[Symbol.hasInstance])
- expectType<{ b: Ref<boolean> }>(objRef.value[Symbol.isConcatSpreadable])
- expectType<Ref<number>[]>(objRef.value[Symbol.iterator])
- expectType<Set<Ref<number>>>(objRef.value[Symbol.match])
- expectType<Map<number, Ref<string>>>(objRef.value[Symbol.matchAll])
- expectType<{ arr: Ref<string>[] }>(objRef.value[Symbol.replace])
- expectType<{ set: Set<Ref<number>> }>(objRef.value[Symbol.search])
- expectType<{ map: Map<number, Ref<string>> }>(objRef.value[Symbol.species])
- expectType<WeakSet<Ref<boolean>>>(objRef.value[Symbol.split])
- expectType<WeakMap<Ref<boolean>, string>>(objRef.value[Symbol.toPrimitive])
- expectType<{ weakSet: WeakSet<Ref<boolean>> }>(
- objRef.value[Symbol.toStringTag],
- )
- expectType<{ weakMap: WeakMap<Ref<boolean>, string> }>(
- objRef.value[Symbol.unscopables],
- )
- expectType<{ arr: Ref<number>[] }>(objRef.value[customSymbol])
- }
- withSymbol()
- const state = reactive({
- foo: {
- value: 1,
- label: 'bar',
- },
- })
- expectType<string>(state.foo.label)
- describe('ref with generic', <T extends { name: string }>() => {
- const r = {} as T
- const s = ref(r)
- expectType<string>(s.value.name)
- const rr = {} as MaybeRef<T>
- // should at least allow casting
- const ss = ref(rr) as Ref<T>
- expectType<string>(ss.value.name)
- })
- describe('allow getter and setter types to be unrelated', <T>() => {
- const a = { b: ref(0) }
- const c = ref(a)
- c.value = a
- const d = {} as T
- const e = ref(d)
- e.value = d
- const f = ref(ref(0))
- expectType<number>(f.value)
- // @ts-expect-error
- f.value = ref(1)
- })
- describe('correctly unwraps nested refs', () => {
- const obj = {
- n: 24,
- ref: ref(24),
- nestedRef: ref({ n: ref(0) }),
- }
- const a = ref(obj)
- expectType<number>(a.value.n)
- expectType<number>(a.value.ref)
- expectType<number>(a.value.nestedRef.n)
- const b = reactive({ a })
- expectType<number>(b.a.n)
- expectType<number>(b.a.ref)
- expectType<number>(b.a.nestedRef.n)
- })
- // computed
- describe('allow computed getter and setter types to be unrelated', () => {
- const obj = ref({
- name: 'foo',
- })
- const c = computed({
- get() {
- return JSON.stringify(obj.value)
- },
- set(val: typeof obj.value) {
- obj.value = val
- },
- })
- c.value = { name: 'bar' } // object
- expectType<string>(c.value)
- })
- describe('Type safety for `WritableComputedRef` and `ComputedRef`', () => {
- // @ts-expect-error
- const writableComputed: WritableComputedRef<string> = computed(() => '')
- // should allow
- const immutableComputed: ComputedRef<string> = writableComputed
- expectType<ComputedRef<string>>(immutableComputed)
- })
- // shallowRef
- type Status = 'initial' | 'ready' | 'invalidating'
- const shallowStatus = shallowRef<Status>('initial')
- if (shallowStatus.value === 'initial') {
- expectType<Ref<Status>>(shallowStatus)
- expectType<Status>(shallowStatus.value)
- shallowStatus.value = 'invalidating'
- }
- const refStatus = ref<Status>('initial')
- if (refStatus.value === 'initial') {
- expectType<Ref<Status>>(shallowStatus)
- expectType<Status>(shallowStatus.value)
- refStatus.value = 'invalidating'
- }
- {
- const shallow = shallowRef(1)
- expectType<Ref<number>>(shallow)
- expectType<ShallowRef<number>>(shallow)
- }
- {
- //#7852
- type Steps = { step: '1' } | { step: '2' }
- const shallowUnionGenParam = shallowRef<Steps>({ step: '1' })
- const shallowUnionAsCast = shallowRef({ step: '1' } as Steps)
- expectType<IsUnion<typeof shallowUnionGenParam>>(false)
- expectType<IsUnion<typeof shallowUnionAsCast>>(false)
- }
- {
- // any value should return Ref<any>, not any
- const a = shallowRef(1 as any)
- expectType<IsAny<typeof a>>(false)
- }
- describe('shallowRef with generic', <T extends { name: string }>() => {
- const r = {} as T
- const s = shallowRef(r)
- expectType<string>(s.value.name)
- expectType<ShallowRef<T>>(shallowRef(r))
- const rr = {} as MaybeRef<T>
- // should at least allow casting
- const ss = shallowRef(rr) as Ref<T> | ShallowRef<T>
- expectType<string>(ss.value.name)
- })
- {
- // should return ShallowRef<T> | Ref<T>, not ShallowRef<T | Ref<T>>
- expectType<ShallowRef<{ name: string }> | Ref<{ name: string }>>(
- shallowRef({} as MaybeRef<{ name: string }>),
- )
- expectType<ShallowRef<number> | Ref<string[]> | ShallowRef<string>>(
- shallowRef('' as Ref<string[]> | string | number),
- )
- }
- // proxyRefs: should return `reactive` directly
- const r1 = reactive({
- k: 'v',
- })
- const p1 = proxyRefs(r1)
- expectType<typeof r1>(p1)
- // proxyRefs: `ShallowUnwrapRef`
- const r2 = {
- a: ref(1),
- c: computed(() => 1),
- u: undefined,
- obj: {
- k: ref('foo'),
- },
- union: Math.random() > 0 - 5 ? ref({ name: 'yo' }) : null,
- }
- const p2 = proxyRefs(r2)
- expectType<number>(p2.a)
- expectType<number>(p2.c)
- expectType<undefined>(p2.u)
- expectType<Ref<string>>(p2.obj.k)
- expectType<{ name: string } | null>(p2.union)
- const r3 = shallowReactive({
- n: ref(1),
- })
- const p3 = proxyRefs(r3)
- expectType<Ref<number>>(p3.n)
- // toRef and toRefs
- {
- const obj: {
- a: number
- b: Ref<number>
- c: number | string
- } = {
- a: 1,
- b: ref(1),
- c: 1,
- }
- // toRef
- expectType<Ref<number>>(toRef(obj, 'a'))
- expectType<Ref<number>>(toRef(obj, 'b'))
- // Should not distribute Refs over union
- expectType<Ref<number | string>>(toRef(obj, 'c'))
- const array = reactive(['a', 'b'])
- expectType<Ref<string>>(toRef(array, '1'))
- expectType<Ref<string>>(toRef(array, '1', 'fallback'))
- const tuple: [string, number] = ['a', 1]
- expectType<Ref<string>>(toRef(tuple, '0'))
- expectType<Ref<number>>(toRef(tuple, '1'))
- expectType<Ref<number>>(toRef(() => 123))
- expectType<Ref<number | string>>(toRef(() => obj.c))
- const r = toRef(() => 123)
- // @ts-expect-error
- r.value = 234
- // toRefs
- expectType<{
- a: Ref<number>
- b: Ref<number>
- // Should not distribute Refs over union
- c: Ref<number | string>
- }>(toRefs(obj))
- // Both should not do any unwrapping
- const someReactive = shallowReactive({
- a: {
- b: ref(42),
- },
- })
- const toRefResult = toRef(someReactive, 'a')
- const toRefsResult = toRefs(someReactive)
- expectType<Ref<number>>(toRefResult.value.b)
- expectType<Ref<number>>(toRefsResult.a.value.b)
- // #5188
- const props = { foo: 1 } as { foo: any }
- const { foo } = toRefs(props)
- expectType<Ref<any>>(foo)
- }
- // toRef default value
- {
- const obj: { x?: number } = {}
- const x = toRef(obj, 'x', 1)
- expectType<Ref<number>>(x)
- }
- // readonly() + ref()
- expectType<Readonly<Ref<number>>>(readonly(ref(1)))
- // #2687
- interface AppData {
- state: 'state1' | 'state2' | 'state3'
- }
- const data: ToRefs<AppData> = toRefs(
- reactive({
- state: 'state1',
- }),
- )
- switch (data.state.value) {
- case 'state1':
- data.state.value = 'state2'
- break
- case 'state2':
- data.state.value = 'state3'
- break
- case 'state3':
- data.state.value = 'state1'
- break
- }
- // #3954
- function testUnrefGenerics<T>(p: T | Ref<T>) {
- expectType<T>(unref(p))
- }
- testUnrefGenerics(1)
- // #4771
- describe('shallow reactive in reactive', () => {
- const baz = reactive({
- foo: shallowReactive({
- a: {
- b: ref(42),
- },
- }),
- })
- const foo = toRef(baz, 'foo')
- expectType<Ref<number>>(foo.value.a.b)
- expectType<number>(foo.value.a.b.value)
- })
- describe('shallow reactive collection in reactive', () => {
- const baz = reactive({
- foo: shallowReactive(new Map([['a', ref(42)]])),
- })
- const foo = toRef(baz, 'foo')
- expectType<Ref<number> | undefined>(foo.value.get('a'))
- })
- 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)
- })
- describe('toRef <-> toValue', () => {
- function foo(
- a: MaybeRef<string>,
- b: () => string,
- c: MaybeRefOrGetter<string>,
- d: ComputedRef<string>,
- ) {
- const r = toRef(a)
- expectType<Ref<string>>(r)
- // writable
- r.value = 'foo'
- const rb = toRef(b)
- expectType<Readonly<Ref<string>>>(rb)
- // @ts-expect-error ref created from getter should be readonly
- rb.value = 'foo'
- const rc = toRef(c)
- expectType<Readonly<Ref<string> | Ref<string>>>(rc)
- // @ts-expect-error ref created from MaybeReadonlyRef should be readonly
- rc.value = 'foo'
- const rd = toRef(d)
- expectType<ComputedRef<string>>(rd)
- // @ts-expect-error ref created from computed ref should be readonly
- rd.value = 'foo'
- expectType<string>(toValue(a))
- expectType<string>(toValue(b))
- expectType<string>(toValue(c))
- expectType<string>(toValue(d))
- return {
- r: toValue(r),
- rb: toValue(rb),
- rc: toValue(rc),
- rd: toValue(rd),
- }
- }
- expectType<{
- r: string
- rb: string
- rc: string
- rd: string
- }>(
- foo(
- 'foo',
- () => 'bar',
- ref('baz'),
- computed(() => 'hi'),
- ),
- )
- })
- // unref
- // #8747
- declare const unref1: number | Ref<number> | ComputedRef<number>
- expectType<number>(unref(unref1))
- // #11356
- declare const unref2:
- | MaybeRef<string>
- | ShallowRef<string>
- | ComputedRef<string>
- | WritableComputedRef<string>
- expectType<string>(unref(unref2))
- // toValue
- expectType<number>(toValue(unref1))
- expectType<string>(toValue(unref2))
- // useTemplateRef
- const tRef = useTemplateRef('foo')
- expectType<TemplateRef>(tRef)
- const tRef2 = useTemplateRef<HTMLElement>('bar')
- expectType<TemplateRef<HTMLElement>>(tRef2)
- // #14637 customRef with different getter/setter types
- describe('customRef with different getter/setter types', () => {
- // customRef should support different getter/setter types like Ref<T, S>
- const cr = customRef<string, number>((track, trigger) => ({
- get: () => 'hello',
- set: (val: number) => {
- // setter accepts number, getter returns string
- trigger()
- },
- }))
- // getter returns string
- expectType<string>(cr.value)
- // setter accepts number
- cr.value = 123
- // @ts-expect-error setter doesn't accept string
- cr.value = 'world'
- })
|