UNPKG

3.35 kBPlain TextView Raw
1import {
2 $mobx,
3 asObservableObject,
4 AnnotationsMap,
5 endBatch,
6 startBatch,
7 CreateObservableOptions,
8 ObservableObjectAdministration,
9 collectStoredAnnotations,
10 isPlainObject,
11 isObservableObject,
12 die,
13 ownKeys,
14 extendObservable,
15 addHiddenProp,
16 storedAnnotationsSymbol
17} from "../internal"
18
19// Hack based on https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-322267089
20// We need this, because otherwise, AdditionalKeys is going to be inferred to be any
21// set of superfluous keys. But, we rather want to get a compile error unless AdditionalKeys is
22// _explicity_ passed as generic argument
23// Fixes: https://github.com/mobxjs/mobx/issues/2325#issuecomment-691070022
24type NoInfer<T> = [T][T extends any ? 0 : never]
25
26export function makeObservable<T extends object, AdditionalKeys extends PropertyKey = never>(
27 target: T,
28 annotations?: AnnotationsMap<T, NoInfer<AdditionalKeys>>,
29 options?: CreateObservableOptions
30): T {
31 const adm: ObservableObjectAdministration = asObservableObject(target, options)[$mobx]
32 startBatch()
33 try {
34 if (__DEV__ && annotations && target[storedAnnotationsSymbol]) {
35 die(
36 `makeObservable second arg must be nullish when using decorators. Mixing @decorator syntax with annotations is not supported.`
37 )
38 }
39 // Default to decorators
40 annotations ??= collectStoredAnnotations(target)
41
42 // Annotate
43 ownKeys(annotations).forEach(key => adm.make_(key, annotations![key]))
44 } finally {
45 endBatch()
46 }
47 return target
48}
49
50// proto[keysSymbol] = new Set<PropertyKey>()
51const keysSymbol = Symbol("mobx-keys")
52
53export function makeAutoObservable<T extends object, AdditionalKeys extends PropertyKey = never>(
54 target: T,
55 overrides?: AnnotationsMap<T, NoInfer<AdditionalKeys>>,
56 options?: CreateObservableOptions
57): T {
58 if (__DEV__) {
59 if (!isPlainObject(target) && !isPlainObject(Object.getPrototypeOf(target)))
60 die(`'makeAutoObservable' can only be used for classes that don't have a superclass`)
61 if (isObservableObject(target))
62 die(`makeAutoObservable can only be used on objects not already made observable`)
63 }
64
65 // Optimization: avoid visiting protos
66 // Assumes that annotation.make_/.extend_ works the same for plain objects
67 if (isPlainObject(target)) {
68 return extendObservable(target, target, overrides, options)
69 }
70
71 const adm: ObservableObjectAdministration = asObservableObject(target, options)[$mobx]
72
73 // Optimization: cache keys on proto
74 // Assumes makeAutoObservable can be called only once per object and can't be used in subclass
75 if (!target[keysSymbol]) {
76 const proto = Object.getPrototypeOf(target)
77 const keys = new Set([...ownKeys(target), ...ownKeys(proto)])
78 keys.delete("constructor")
79 keys.delete($mobx)
80 addHiddenProp(proto, keysSymbol, keys)
81 }
82
83 startBatch()
84 try {
85 target[keysSymbol].forEach(key =>
86 adm.make_(
87 key,
88 // must pass "undefined" for { key: undefined }
89 !overrides ? true : key in overrides ? overrides[key] : true
90 )
91 )
92 } finally {
93 endBatch()
94 }
95 return target
96}