ref.test-d.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. import {
  2. type ComputedRef,
  3. type MaybeRef,
  4. type MaybeRefOrGetter,
  5. type Ref,
  6. type ShallowRef,
  7. type ToRefs,
  8. computed,
  9. isRef,
  10. proxyRefs,
  11. reactive,
  12. readonly,
  13. ref,
  14. shallowReactive,
  15. shallowRef,
  16. toRef,
  17. toRefs,
  18. toValue,
  19. unref,
  20. } from 'vue'
  21. import { type IsAny, type IsUnion, describe, expectType } 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. const nestedRef = ref({ foo: ref(document.createElement('DIV')) })
  88. expectType<Ref<{ foo: HTMLElement }>>(nestedRef)
  89. expectType<{ foo: HTMLElement }>(nestedRef.value)
  90. }
  91. const el = document.createElement('DIV')
  92. bailType(el)
  93. function withSymbol() {
  94. const customSymbol = Symbol()
  95. const obj = {
  96. [Symbol.asyncIterator]: ref(1),
  97. [Symbol.hasInstance]: { a: ref('a') },
  98. [Symbol.isConcatSpreadable]: { b: ref(true) },
  99. [Symbol.iterator]: [ref(1)],
  100. [Symbol.match]: new Set<Ref<number>>(),
  101. [Symbol.matchAll]: new Map<number, Ref<string>>(),
  102. [Symbol.replace]: { arr: [ref('a')] },
  103. [Symbol.search]: { set: new Set<Ref<number>>() },
  104. [Symbol.species]: { map: new Map<number, Ref<string>>() },
  105. [Symbol.split]: new WeakSet<Ref<boolean>>(),
  106. [Symbol.toPrimitive]: new WeakMap<Ref<boolean>, string>(),
  107. [Symbol.toStringTag]: { weakSet: new WeakSet<Ref<boolean>>() },
  108. [Symbol.unscopables]: { weakMap: new WeakMap<Ref<boolean>, string>() },
  109. [customSymbol]: { arr: [ref(1)] },
  110. }
  111. const objRef = ref(obj)
  112. expectType<Ref<number>>(objRef.value[Symbol.asyncIterator])
  113. expectType<{ a: Ref<string> }>(objRef.value[Symbol.hasInstance])
  114. expectType<{ b: Ref<boolean> }>(objRef.value[Symbol.isConcatSpreadable])
  115. expectType<Ref<number>[]>(objRef.value[Symbol.iterator])
  116. expectType<Set<Ref<number>>>(objRef.value[Symbol.match])
  117. expectType<Map<number, Ref<string>>>(objRef.value[Symbol.matchAll])
  118. expectType<{ arr: Ref<string>[] }>(objRef.value[Symbol.replace])
  119. expectType<{ set: Set<Ref<number>> }>(objRef.value[Symbol.search])
  120. expectType<{ map: Map<number, Ref<string>> }>(objRef.value[Symbol.species])
  121. expectType<WeakSet<Ref<boolean>>>(objRef.value[Symbol.split])
  122. expectType<WeakMap<Ref<boolean>, string>>(objRef.value[Symbol.toPrimitive])
  123. expectType<{ weakSet: WeakSet<Ref<boolean>> }>(
  124. objRef.value[Symbol.toStringTag],
  125. )
  126. expectType<{ weakMap: WeakMap<Ref<boolean>, string> }>(
  127. objRef.value[Symbol.unscopables],
  128. )
  129. expectType<{ arr: Ref<number>[] }>(objRef.value[customSymbol])
  130. }
  131. withSymbol()
  132. const state = reactive({
  133. foo: {
  134. value: 1,
  135. label: 'bar',
  136. },
  137. })
  138. expectType<string>(state.foo.label)
  139. describe('ref with generic', <T extends { name: string }>() => {
  140. const r = {} as T
  141. const s = ref(r)
  142. expectType<string>(s.value.name)
  143. const rr = {} as MaybeRef<T>
  144. // should at least allow casting
  145. const ss = ref(rr) as Ref<T>
  146. expectType<string>(ss.value.name)
  147. })
  148. describe('allow getter and setter types to be unrelated', <T>() => {
  149. const a = { b: ref(0) }
  150. const c = ref(a)
  151. c.value = a
  152. const d = {} as T
  153. const e = ref(d)
  154. e.value = d
  155. const f = ref(ref(0))
  156. expectType<number>(f.value)
  157. // @ts-expect-error
  158. f.value = ref(1)
  159. })
  160. // shallowRef
  161. type Status = 'initial' | 'ready' | 'invalidating'
  162. const shallowStatus = shallowRef<Status>('initial')
  163. if (shallowStatus.value === 'initial') {
  164. expectType<Ref<Status>>(shallowStatus)
  165. expectType<Status>(shallowStatus.value)
  166. shallowStatus.value = 'invalidating'
  167. }
  168. const refStatus = ref<Status>('initial')
  169. if (refStatus.value === 'initial') {
  170. expectType<Ref<Status>>(shallowStatus)
  171. expectType<Status>(shallowStatus.value)
  172. refStatus.value = 'invalidating'
  173. }
  174. {
  175. const shallow = shallowRef(1)
  176. expectType<Ref<number>>(shallow)
  177. expectType<ShallowRef<number>>(shallow)
  178. }
  179. {
  180. //#7852
  181. type Steps = { step: '1' } | { step: '2' }
  182. const shallowUnionGenParam = shallowRef<Steps>({ step: '1' })
  183. const shallowUnionAsCast = shallowRef({ step: '1' } as Steps)
  184. expectType<IsUnion<typeof shallowUnionGenParam>>(false)
  185. expectType<IsUnion<typeof shallowUnionAsCast>>(false)
  186. }
  187. {
  188. // any value should return Ref<any>, not any
  189. const a = shallowRef(1 as any)
  190. expectType<IsAny<typeof a>>(false)
  191. }
  192. describe('shallowRef with generic', <T extends { name: string }>() => {
  193. const r = {} as T
  194. const s = shallowRef(r)
  195. expectType<string>(s.value.name)
  196. expectType<ShallowRef<T>>(shallowRef(r))
  197. const rr = {} as MaybeRef<T>
  198. // should at least allow casting
  199. const ss = shallowRef(rr) as Ref<T> | ShallowRef<T>
  200. expectType<string>(ss.value.name)
  201. })
  202. {
  203. // should return ShallowRef<T> | Ref<T>, not ShallowRef<T | Ref<T>>
  204. expectType<ShallowRef<{ name: string }> | Ref<{ name: string }>>(
  205. shallowRef({} as MaybeRef<{ name: string }>),
  206. )
  207. expectType<ShallowRef<number> | Ref<string[]> | ShallowRef<string>>(
  208. shallowRef('' as Ref<string[]> | string | number),
  209. )
  210. }
  211. // proxyRefs: should return `reactive` directly
  212. const r1 = reactive({
  213. k: 'v',
  214. })
  215. const p1 = proxyRefs(r1)
  216. expectType<typeof r1>(p1)
  217. // proxyRefs: `ShallowUnwrapRef`
  218. const r2 = {
  219. a: ref(1),
  220. c: computed(() => 1),
  221. u: undefined,
  222. obj: {
  223. k: ref('foo'),
  224. },
  225. union: Math.random() > 0 - 5 ? ref({ name: 'yo' }) : null,
  226. }
  227. const p2 = proxyRefs(r2)
  228. expectType<number>(p2.a)
  229. expectType<number>(p2.c)
  230. expectType<undefined>(p2.u)
  231. expectType<Ref<string>>(p2.obj.k)
  232. expectType<{ name: string } | null>(p2.union)
  233. // toRef and toRefs
  234. {
  235. const obj: {
  236. a: number
  237. b: Ref<number>
  238. c: number | string
  239. } = {
  240. a: 1,
  241. b: ref(1),
  242. c: 1,
  243. }
  244. // toRef
  245. expectType<Ref<number>>(toRef(obj, 'a'))
  246. expectType<Ref<number>>(toRef(obj, 'b'))
  247. // Should not distribute Refs over union
  248. expectType<Ref<number | string>>(toRef(obj, 'c'))
  249. expectType<Ref<number>>(toRef(() => 123))
  250. expectType<Ref<number | string>>(toRef(() => obj.c))
  251. const r = toRef(() => 123)
  252. // @ts-expect-error
  253. r.value = 234
  254. // toRefs
  255. expectType<{
  256. a: Ref<number>
  257. b: Ref<number>
  258. // Should not distribute Refs over union
  259. c: Ref<number | string>
  260. }>(toRefs(obj))
  261. // Both should not do any unwrapping
  262. const someReactive = shallowReactive({
  263. a: {
  264. b: ref(42),
  265. },
  266. })
  267. const toRefResult = toRef(someReactive, 'a')
  268. const toRefsResult = toRefs(someReactive)
  269. expectType<Ref<number>>(toRefResult.value.b)
  270. expectType<Ref<number>>(toRefsResult.a.value.b)
  271. // #5188
  272. const props = { foo: 1 } as { foo: any }
  273. const { foo } = toRefs(props)
  274. expectType<Ref<any>>(foo)
  275. }
  276. // toRef default value
  277. {
  278. const obj: { x?: number } = {}
  279. const x = toRef(obj, 'x', 1)
  280. expectType<Ref<number>>(x)
  281. }
  282. // readonly() + ref()
  283. expectType<Readonly<Ref<number>>>(readonly(ref(1)))
  284. // #2687
  285. interface AppData {
  286. state: 'state1' | 'state2' | 'state3'
  287. }
  288. const data: ToRefs<AppData> = toRefs(
  289. reactive({
  290. state: 'state1',
  291. }),
  292. )
  293. switch (data.state.value) {
  294. case 'state1':
  295. data.state.value = 'state2'
  296. break
  297. case 'state2':
  298. data.state.value = 'state3'
  299. break
  300. case 'state3':
  301. data.state.value = 'state1'
  302. break
  303. }
  304. // #3954
  305. function testUnrefGenerics<T>(p: T | Ref<T>) {
  306. expectType<T>(unref(p))
  307. }
  308. testUnrefGenerics(1)
  309. // #4771
  310. describe('shallow reactive in reactive', () => {
  311. const baz = reactive({
  312. foo: shallowReactive({
  313. a: {
  314. b: ref(42),
  315. },
  316. }),
  317. })
  318. const foo = toRef(baz, 'foo')
  319. expectType<Ref<number>>(foo.value.a.b)
  320. expectType<number>(foo.value.a.b.value)
  321. })
  322. describe('shallow ref in reactive', () => {
  323. const x = reactive({
  324. foo: shallowRef({
  325. bar: {
  326. baz: ref(123),
  327. qux: reactive({
  328. z: ref(123),
  329. }),
  330. },
  331. }),
  332. })
  333. expectType<Ref<number>>(x.foo.bar.baz)
  334. expectType<number>(x.foo.bar.qux.z)
  335. })
  336. describe('ref in shallow ref', () => {
  337. const x = shallowRef({
  338. a: ref(123),
  339. })
  340. expectType<Ref<number>>(x.value.a)
  341. })
  342. describe('reactive in shallow ref', () => {
  343. const x = shallowRef({
  344. a: reactive({
  345. b: ref(0),
  346. }),
  347. })
  348. expectType<number>(x.value.a.b)
  349. })
  350. describe('toRef <-> toValue', () => {
  351. function foo(
  352. a: MaybeRef<string>,
  353. b: () => string,
  354. c: MaybeRefOrGetter<string>,
  355. d: ComputedRef<string>,
  356. ) {
  357. const r = toRef(a)
  358. expectType<Ref<string>>(r)
  359. // writable
  360. r.value = 'foo'
  361. const rb = toRef(b)
  362. expectType<Readonly<Ref<string>>>(rb)
  363. // @ts-expect-error ref created from getter should be readonly
  364. rb.value = 'foo'
  365. const rc = toRef(c)
  366. expectType<Readonly<Ref<string> | Ref<string>>>(rc)
  367. // @ts-expect-error ref created from MaybeReadonlyRef should be readonly
  368. rc.value = 'foo'
  369. const rd = toRef(d)
  370. expectType<ComputedRef<string>>(rd)
  371. // @ts-expect-error ref created from computed ref should be readonly
  372. rd.value = 'foo'
  373. expectType<string>(toValue(a))
  374. expectType<string>(toValue(b))
  375. expectType<string>(toValue(c))
  376. expectType<string>(toValue(d))
  377. return {
  378. r: toValue(r),
  379. rb: toValue(rb),
  380. rc: toValue(rc),
  381. rd: toValue(rd),
  382. }
  383. }
  384. expectType<{
  385. r: string
  386. rb: string
  387. rc: string
  388. rd: string
  389. }>(
  390. foo(
  391. 'foo',
  392. () => 'bar',
  393. ref('baz'),
  394. computed(() => 'hi'),
  395. ),
  396. )
  397. })
  398. // unref
  399. declare const text: ShallowRef<string> | ComputedRef<string> | MaybeRef<string>
  400. expectType<string>(unref(text))