ref.test-d.ts 12 KB

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