ref.test-d.ts 11 KB

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