reactivity-test.ts 8.6 KB

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