ref.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import { defineReactive } from 'core/observer/index'
  2. import { isReactive, type ShallowReactiveMarker } from './reactive'
  3. import type { IfAny } from 'typescript/utils'
  4. import Dep from 'core/observer/dep'
  5. import { warn, isArray } from 'core/util'
  6. declare const RefSymbol: unique symbol
  7. export declare const RawSymbol: unique symbol
  8. export interface Ref<T = any> {
  9. value: T
  10. /**
  11. * Type differentiator only.
  12. * We need this to be in public d.ts but don't want it to show up in IDE
  13. * autocomplete, so we use a private Symbol instead.
  14. */
  15. [RefSymbol]: true
  16. /**
  17. * @private
  18. */
  19. dep?: Dep
  20. }
  21. export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
  22. export function isRef(r: any): r is Ref {
  23. return !!(r && r.__v_isRef === true)
  24. }
  25. export function ref<T extends object>(
  26. value: T
  27. ): [T] extends [Ref] ? T : Ref<UnwrapRef<T>>
  28. export function ref<T>(value: T): Ref<UnwrapRef<T>>
  29. export function ref<T = any>(): Ref<T | undefined>
  30. export function ref(value?: unknown) {
  31. return createRef(value, false)
  32. }
  33. declare const ShallowRefMarker: unique symbol
  34. export type ShallowRef<T = any> = Ref<T> & { [ShallowRefMarker]?: true }
  35. export function shallowRef<T extends object>(
  36. value: T
  37. ): T extends Ref ? T : ShallowRef<T>
  38. export function shallowRef<T>(value: T): ShallowRef<T>
  39. export function shallowRef<T = any>(): ShallowRef<T | undefined>
  40. export function shallowRef(value?: unknown) {
  41. return createRef(value, true)
  42. }
  43. function createRef(rawValue: unknown, shallow: boolean) {
  44. if (isRef(rawValue)) {
  45. return rawValue
  46. }
  47. const ref: any = { __v_isRef: true, __v_isShallow: shallow }
  48. ref.dep = defineReactive(ref, 'value', rawValue, null, shallow)
  49. return ref
  50. }
  51. export function triggerRef(ref: Ref) {
  52. if (__DEV__ && !ref.dep) {
  53. warn(`received object is not a triggerable ref.`)
  54. }
  55. ref.dep && ref.dep.notify()
  56. }
  57. export function unref<T>(ref: T | Ref<T>): T {
  58. return isRef(ref) ? (ref.value as any) : ref
  59. }
  60. export type CustomRefFactory<T> = (
  61. track: () => void,
  62. trigger: () => void
  63. ) => {
  64. get: () => T
  65. set: (value: T) => void
  66. }
  67. class CustomRefImpl<T> {
  68. public dep = new Dep()
  69. private readonly _get: ReturnType<CustomRefFactory<T>>['get']
  70. private readonly _set: ReturnType<CustomRefFactory<T>>['set']
  71. public readonly __v_isRef = true
  72. constructor(factory: CustomRefFactory<T>) {
  73. const { get, set } = factory(
  74. () => this.dep.depend(),
  75. () => this.dep.notify()
  76. )
  77. this._get = get
  78. this._set = set
  79. }
  80. get value() {
  81. return this._get()
  82. }
  83. set value(newVal) {
  84. this._set(newVal)
  85. }
  86. }
  87. export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
  88. return new CustomRefImpl(factory) as any
  89. }
  90. export type ToRefs<T = any> = {
  91. [K in keyof T]: ToRef<T[K]>
  92. }
  93. export function toRefs<T extends object>(object: T): ToRefs<T> {
  94. if (__DEV__ && !isReactive(object)) {
  95. warn(`toRefs() expects a reactive object but received a plain one.`)
  96. }
  97. const ret: any = isArray(object) ? new Array(object.length) : {}
  98. for (const key in object) {
  99. ret[key] = toRef(object, key)
  100. }
  101. return ret
  102. }
  103. class ObjectRefImpl<T extends object, K extends keyof T> {
  104. public readonly __v_isRef = true
  105. constructor(
  106. private readonly _object: T,
  107. private readonly _key: K,
  108. private readonly _defaultValue?: T[K]
  109. ) {}
  110. get value() {
  111. const val = this._object[this._key]
  112. return val === undefined ? (this._defaultValue as T[K]) : val
  113. }
  114. set value(newVal) {
  115. this._object[this._key] = newVal
  116. }
  117. }
  118. export type ToRef<T> = IfAny<T, Ref<T>, [T] extends [Ref] ? T : Ref<T>>
  119. export function toRef<T extends object, K extends keyof T>(
  120. object: T,
  121. key: K
  122. ): ToRef<T[K]>
  123. export function toRef<T extends object, K extends keyof T>(
  124. object: T,
  125. key: K,
  126. defaultValue: T[K]
  127. ): ToRef<Exclude<T[K], undefined>>
  128. export function toRef<T extends object, K extends keyof T>(
  129. object: T,
  130. key: K,
  131. defaultValue?: T[K]
  132. ): ToRef<T[K]> {
  133. const val = object[key]
  134. return isRef(val)
  135. ? val
  136. : (new ObjectRefImpl(object, key, defaultValue) as any)
  137. }
  138. /**
  139. * This is a special exported interface for other packages to declare
  140. * additional types that should bail out for ref unwrapping. For example
  141. * \@vue/runtime-dom can declare it like so in its d.ts:
  142. *
  143. * ``` ts
  144. * declare module 'vue' {
  145. * export interface RefUnwrapBailTypes {
  146. * runtimeDOMBailTypes: Node | Window
  147. * }
  148. * }
  149. * ```
  150. *
  151. * Note that api-extractor somehow refuses to include `declare module`
  152. * augmentations in its generated d.ts, so we have to manually append them
  153. * to the final generated d.ts in our build process.
  154. */
  155. export interface RefUnwrapBailTypes {}
  156. export type ShallowUnwrapRef<T> = {
  157. [K in keyof T]: T[K] extends Ref<infer V>
  158. ? V
  159. : // if `V` is `unknown` that means it does not extend `Ref` and is undefined
  160. T[K] extends Ref<infer V> | undefined
  161. ? unknown extends V
  162. ? undefined
  163. : V | undefined
  164. : T[K]
  165. }
  166. export type UnwrapRef<T> = T extends ShallowRef<infer V>
  167. ? V
  168. : T extends Ref<infer V>
  169. ? UnwrapRefSimple<V>
  170. : UnwrapRefSimple<T>
  171. type BaseTypes = string | number | boolean
  172. type CollectionTypes = IterableCollections | WeakCollections
  173. type IterableCollections = Map<any, any> | Set<any>
  174. type WeakCollections = WeakMap<any, any> | WeakSet<any>
  175. export type UnwrapRefSimple<T> = T extends
  176. | Function
  177. | CollectionTypes
  178. | BaseTypes
  179. | Ref
  180. | RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
  181. | { [RawSymbol]?: true }
  182. ? T
  183. : T extends Array<any>
  184. ? { [K in keyof T]: UnwrapRefSimple<T[K]> }
  185. : T extends object & { [ShallowReactiveMarker]?: never }
  186. ? {
  187. [P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>
  188. }
  189. : T