ref.test-d.ts 10 KB

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