ref.test-d.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. import {
  2. Ref,
  3. ref,
  4. shallowRef,
  5. isRef,
  6. unref,
  7. reactive,
  8. proxyRefs,
  9. toRef,
  10. toValue,
  11. toRefs,
  12. ToRefs,
  13. shallowReactive,
  14. readonly,
  15. MaybeRef,
  16. MaybeRefOrGetter,
  17. ComputedRef,
  18. computed
  19. } from 'vue'
  20. import { expectType, describe } from './utils'
  21. function plainType(arg: number | Ref<number>) {
  22. // ref coercing
  23. const coerced = ref(arg)
  24. expectType<Ref<number>>(coerced)
  25. // isRef as type guard
  26. if (isRef(arg)) {
  27. expectType<Ref<number>>(arg)
  28. }
  29. // ref unwrapping
  30. expectType<number>(unref(arg))
  31. expectType<number>(toValue(arg))
  32. expectType<number>(toValue(() => 123))
  33. // ref inner type should be unwrapped
  34. const nestedRef = ref({
  35. foo: ref(1)
  36. })
  37. expectType<{ foo: number }>(nestedRef.value)
  38. // ref boolean
  39. const falseRef = ref(false)
  40. expectType<Ref<boolean>>(falseRef)
  41. expectType<boolean>(falseRef.value)
  42. // ref true
  43. const trueRef = ref<true>(true)
  44. expectType<Ref<true>>(trueRef)
  45. expectType<true>(trueRef.value)
  46. // tuple
  47. expectType<[number, string]>(unref(ref([1, '1'])))
  48. interface IteratorFoo {
  49. [Symbol.iterator]: any
  50. }
  51. // with symbol
  52. expectType<Ref<IteratorFoo | null | undefined>>(
  53. ref<IteratorFoo | null | undefined>()
  54. )
  55. // should not unwrap ref inside arrays
  56. const arr = ref([1, new Map<string, any>(), ref('1')]).value
  57. const value = arr[0]
  58. if (isRef(value)) {
  59. expectType<Ref>(value)
  60. } else if (typeof value === 'number') {
  61. expectType<number>(value)
  62. } else {
  63. // should narrow down to Map type
  64. // and not contain any Ref type
  65. expectType<Map<string, any>>(value)
  66. }
  67. // should still unwrap in objects nested in arrays
  68. const arr2 = ref([{ a: ref(1) }]).value
  69. expectType<number>(arr2[0].a)
  70. }
  71. plainType(1)
  72. function bailType(arg: HTMLElement | Ref<HTMLElement>) {
  73. // ref coercing
  74. const coerced = ref(arg)
  75. expectType<Ref<HTMLElement>>(coerced)
  76. // isRef as type guard
  77. if (isRef(arg)) {
  78. expectType<Ref<HTMLElement>>(arg)
  79. }
  80. // ref unwrapping
  81. expectType<HTMLElement>(unref(arg))
  82. // ref inner type should be unwrapped
  83. // eslint-disable-next-line no-restricted-globals
  84. const nestedRef = ref({ foo: ref(document.createElement('DIV')) })
  85. expectType<Ref<{ foo: HTMLElement }>>(nestedRef)
  86. expectType<{ foo: HTMLElement }>(nestedRef.value)
  87. }
  88. // eslint-disable-next-line no-restricted-globals
  89. const el = document.createElement('DIV')
  90. bailType(el)
  91. function withSymbol() {
  92. const customSymbol = Symbol()
  93. const obj = {
  94. [Symbol.asyncIterator]: ref(1),
  95. [Symbol.hasInstance]: { a: ref('a') },
  96. [Symbol.isConcatSpreadable]: { b: ref(true) },
  97. [Symbol.iterator]: [ref(1)],
  98. [Symbol.match]: new Set<Ref<number>>(),
  99. [Symbol.matchAll]: new Map<number, Ref<string>>(),
  100. [Symbol.replace]: { arr: [ref('a')] },
  101. [Symbol.search]: { set: new Set<Ref<number>>() },
  102. [Symbol.species]: { map: new Map<number, Ref<string>>() },
  103. [Symbol.split]: new WeakSet<Ref<boolean>>(),
  104. [Symbol.toPrimitive]: new WeakMap<Ref<boolean>, string>(),
  105. [Symbol.toStringTag]: { weakSet: new WeakSet<Ref<boolean>>() },
  106. [Symbol.unscopables]: { weakMap: new WeakMap<Ref<boolean>, string>() },
  107. [customSymbol]: { arr: [ref(1)] }
  108. }
  109. const objRef = ref(obj)
  110. expectType<Ref<number>>(objRef.value[Symbol.asyncIterator])
  111. expectType<{ a: Ref<string> }>(objRef.value[Symbol.hasInstance])
  112. expectType<{ b: Ref<boolean> }>(objRef.value[Symbol.isConcatSpreadable])
  113. expectType<Ref<number>[]>(objRef.value[Symbol.iterator])
  114. expectType<Set<Ref<number>>>(objRef.value[Symbol.match])
  115. expectType<Map<number, Ref<string>>>(objRef.value[Symbol.matchAll])
  116. expectType<{ arr: Ref<string>[] }>(objRef.value[Symbol.replace])
  117. expectType<{ set: Set<Ref<number>> }>(objRef.value[Symbol.search])
  118. expectType<{ map: Map<number, Ref<string>> }>(objRef.value[Symbol.species])
  119. expectType<WeakSet<Ref<boolean>>>(objRef.value[Symbol.split])
  120. expectType<WeakMap<Ref<boolean>, string>>(objRef.value[Symbol.toPrimitive])
  121. expectType<{ weakSet: WeakSet<Ref<boolean>> }>(
  122. objRef.value[Symbol.toStringTag]
  123. )
  124. expectType<{ weakMap: WeakMap<Ref<boolean>, string> }>(
  125. objRef.value[Symbol.unscopables]
  126. )
  127. expectType<{ arr: Ref<number>[] }>(objRef.value[customSymbol])
  128. }
  129. withSymbol()
  130. const state = reactive({
  131. foo: {
  132. value: 1,
  133. label: 'bar'
  134. }
  135. })
  136. expectType<string>(state.foo.label)
  137. // shallowRef
  138. type Status = 'initial' | 'ready' | 'invalidating'
  139. const shallowStatus = shallowRef<Status>('initial')
  140. if (shallowStatus.value === 'initial') {
  141. expectType<Ref<Status>>(shallowStatus)
  142. expectType<Status>(shallowStatus.value)
  143. shallowStatus.value = 'invalidating'
  144. }
  145. const refStatus = ref<Status>('initial')
  146. if (refStatus.value === 'initial') {
  147. expectType<Ref<Status>>(shallowStatus)
  148. expectType<Status>(shallowStatus.value)
  149. refStatus.value = 'invalidating'
  150. }
  151. // proxyRefs: should return `reactive` directly
  152. const r1 = reactive({
  153. k: 'v'
  154. })
  155. const p1 = proxyRefs(r1)
  156. expectType<typeof r1>(p1)
  157. // proxyRefs: `ShallowUnwrapRef`
  158. const r2 = {
  159. a: ref(1),
  160. obj: {
  161. k: ref('foo')
  162. }
  163. }
  164. const p2 = proxyRefs(r2)
  165. expectType<number>(p2.a)
  166. expectType<Ref<string>>(p2.obj.k)
  167. // toRef and toRefs
  168. {
  169. const obj: {
  170. a: number
  171. b: Ref<number>
  172. c: number | string
  173. } = {
  174. a: 1,
  175. b: ref(1),
  176. c: 1
  177. }
  178. // toRef
  179. expectType<Ref<number>>(toRef(obj, 'a'))
  180. expectType<Ref<number>>(toRef(obj, 'b'))
  181. // Should not distribute Refs over union
  182. expectType<Ref<number | string>>(toRef(obj, 'c'))
  183. expectType<Ref<number>>(toRef(() => 123))
  184. expectType<Ref<number | string>>(toRef(() => obj.c))
  185. const r = toRef(() => 123)
  186. // @ts-expect-error
  187. r.value = 234
  188. // toRefs
  189. expectType<{
  190. a: Ref<number>
  191. b: Ref<number>
  192. // Should not distribute Refs over union
  193. c: Ref<number | string>
  194. }>(toRefs(obj))
  195. // Both should not do any unwrapping
  196. const someReactive = shallowReactive({
  197. a: {
  198. b: ref(42)
  199. }
  200. })
  201. const toRefResult = toRef(someReactive, 'a')
  202. const toRefsResult = toRefs(someReactive)
  203. expectType<Ref<number>>(toRefResult.value.b)
  204. expectType<Ref<number>>(toRefsResult.a.value.b)
  205. // #5188
  206. const props = { foo: 1 } as { foo: any }
  207. const { foo } = toRefs(props)
  208. expectType<Ref<any>>(foo)
  209. }
  210. // toRef default value
  211. {
  212. const obj: { x?: number } = {}
  213. const x = toRef(obj, 'x', 1)
  214. expectType<Ref<number>>(x)
  215. }
  216. // readonly() + ref()
  217. expectType<Readonly<Ref<number>>>(readonly(ref(1)))
  218. // #2687
  219. interface AppData {
  220. state: 'state1' | 'state2' | 'state3'
  221. }
  222. const data: ToRefs<AppData> = toRefs(
  223. reactive({
  224. state: 'state1'
  225. })
  226. )
  227. switch (data.state.value) {
  228. case 'state1':
  229. data.state.value = 'state2'
  230. break
  231. case 'state2':
  232. data.state.value = 'state3'
  233. break
  234. case 'state3':
  235. data.state.value = 'state1'
  236. break
  237. }
  238. // #3954
  239. function testUnrefGenerics<T>(p: T | Ref<T>) {
  240. expectType<T>(unref(p))
  241. }
  242. testUnrefGenerics(1)
  243. // #4771
  244. describe('shallow reactive in reactive', () => {
  245. const baz = reactive({
  246. foo: shallowReactive({
  247. a: {
  248. b: ref(42)
  249. }
  250. })
  251. })
  252. const foo = toRef(baz, 'foo')
  253. expectType<Ref<number>>(foo.value.a.b)
  254. expectType<number>(foo.value.a.b.value)
  255. })
  256. describe('shallow ref in reactive', () => {
  257. const x = reactive({
  258. foo: shallowRef({
  259. bar: {
  260. baz: ref(123),
  261. qux: reactive({
  262. z: ref(123)
  263. })
  264. }
  265. })
  266. })
  267. expectType<Ref<number>>(x.foo.bar.baz)
  268. expectType<number>(x.foo.bar.qux.z)
  269. })
  270. describe('ref in shallow ref', () => {
  271. const x = shallowRef({
  272. a: ref(123)
  273. })
  274. expectType<Ref<number>>(x.value.a)
  275. })
  276. describe('reactive in shallow ref', () => {
  277. const x = shallowRef({
  278. a: reactive({
  279. b: ref(0)
  280. })
  281. })
  282. expectType<number>(x.value.a.b)
  283. })
  284. describe('toRef <-> toValue', () => {
  285. function foo(
  286. a: MaybeRef<string>,
  287. b: () => string,
  288. c: MaybeRefOrGetter<string>,
  289. d: ComputedRef<string>
  290. ) {
  291. const r = toRef(a)
  292. expectType<Ref<string>>(r)
  293. // writable
  294. r.value = 'foo'
  295. const rb = toRef(b)
  296. expectType<Readonly<Ref<string>>>(rb)
  297. // @ts-expect-error ref created from getter should be readonly
  298. rb.value = 'foo'
  299. const rc = toRef(c)
  300. expectType<Readonly<Ref<string> | Ref<string>>>(rc)
  301. // @ts-expect-error ref created from MaybeReadonlyRef should be readonly
  302. rc.value = 'foo'
  303. const rd = toRef(d)
  304. expectType<ComputedRef<string>>(rd)
  305. // @ts-expect-error ref created from computed ref should be readonly
  306. rd.value = 'foo'
  307. expectType<string>(toValue(a))
  308. expectType<string>(toValue(b))
  309. expectType<string>(toValue(c))
  310. expectType<string>(toValue(d))
  311. return {
  312. r: toValue(r),
  313. rb: toValue(rb),
  314. rc: toValue(rc),
  315. rd: toValue(rd)
  316. }
  317. }
  318. expectType<{
  319. r: string
  320. rb: string
  321. rc: string
  322. rd: string
  323. }>(
  324. foo(
  325. 'foo',
  326. () => 'bar',
  327. ref('baz'),
  328. computed(() => 'hi')
  329. )
  330. )
  331. })