mobx-keystone 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +1 -1
  3. package/dist/mobx-keystone.esm.js +275 -489
  4. package/dist/mobx-keystone.esm.mjs +275 -489
  5. package/dist/mobx-keystone.umd.js +274 -492
  6. package/dist/types/actionMiddlewares/undoMiddleware.d.ts +2 -10
  7. package/dist/types/modelShared/modelDecorator.d.ts +5 -2
  8. package/dist/types/modelShared/prop.d.ts +42 -29
  9. package/dist/types/ref/Ref.d.ts +1 -4
  10. package/dist/types/snapshot/SnapshotOf.d.ts +4 -4
  11. package/dist/types/snapshot/applySnapshot.d.ts +1 -1
  12. package/dist/types/snapshot/fromSnapshot.d.ts +2 -2
  13. package/dist/types/snapshot/getSnapshot.d.ts +1 -1
  14. package/dist/types/types/index.d.ts +1 -0
  15. package/dist/types/types/schemas.d.ts +41 -37
  16. package/dist/types/types/tProp.d.ts +12 -6
  17. package/dist/types/types/types.d.ts +6 -6
  18. package/dist/types/types/utility/typeToStoredData.d.ts +14 -10
  19. package/dist/types/types/utility/typesCodecCore.d.ts +3 -0
  20. package/dist/types/utils/ModelPool.d.ts +0 -1
  21. package/dist/types/utils/types.d.ts +6 -0
  22. package/dist/types/wrappers/ArraySet.d.ts +1 -0
  23. package/package.json +4 -2
  24. package/src/action/modelFlow.ts +35 -66
  25. package/src/actionMiddlewares/actionTrackingMiddleware.ts +41 -71
  26. package/src/actionMiddlewares/transactionMiddleware.ts +1 -1
  27. package/src/actionMiddlewares/undoMiddleware.ts +16 -22
  28. package/src/context/context.ts +15 -36
  29. package/src/dataModel/utils.ts +5 -28
  30. package/src/deepChange/onDeepChange.ts +44 -126
  31. package/src/model/BaseModel.ts +1 -1
  32. package/src/model/utils.ts +5 -10
  33. package/src/modelShared/modelDecorator.ts +11 -5
  34. package/src/modelShared/prop.ts +115 -111
  35. package/src/modelShared/sharedInternalModel.ts +2 -2
  36. package/src/parent/arrayPaths.ts +1 -4
  37. package/src/parent/path.ts +0 -10
  38. package/src/patch/patchRecorder.ts +13 -28
  39. package/src/ref/core.ts +6 -19
  40. package/src/rootStore/rootStore.ts +1 -3
  41. package/src/snapshot/SnapshotOf.ts +24 -48
  42. package/src/snapshot/applySnapshot.ts +1 -1
  43. package/src/snapshot/fromSnapshot.ts +10 -3
  44. package/src/snapshot/getSnapshot.ts +1 -1
  45. package/src/snapshot/internal.ts +0 -2
  46. package/src/snapshot/reconcileModelSnapshot.ts +5 -3
  47. package/src/snapshot/reconcilePlainObjectSnapshot.ts +3 -1
  48. package/src/treeUtils/draft.ts +6 -1
  49. package/src/treeUtils/sandbox.ts +10 -7
  50. package/src/tweaker/tweakArray.ts +20 -50
  51. package/src/tweaker/tweakPlainObject.ts +9 -15
  52. package/src/types/arrayBased/typesArray.ts +13 -19
  53. package/src/types/arrayBased/typesTuple.ts +16 -25
  54. package/src/types/createCachedTypeCheck.ts +17 -30
  55. package/src/types/index.ts +1 -0
  56. package/src/types/objectBased/typesDataModelData.ts +7 -8
  57. package/src/types/objectBased/typesObject.ts +11 -15
  58. package/src/types/objectBased/typesRecord.ts +11 -15
  59. package/src/types/schemas.ts +59 -71
  60. package/src/types/tProp.ts +29 -29
  61. package/src/types/utility/typeToStoredData.ts +22 -21
  62. package/src/types/utility/typesCodecCore.ts +5 -0
  63. package/src/types/utility/typesCodecSupport.ts +3 -3
  64. package/src/types/utility/typesMap.ts +43 -51
  65. package/src/types/utility/typesRefinement.ts +7 -8
  66. package/src/types/utility/typesSkipCheck.ts +7 -8
  67. package/src/types/utility/typesTag.ts +7 -8
  68. package/src/utils/ModelPool.ts +1 -18
  69. package/src/utils/decorators.ts +5 -5
  70. package/src/utils/index.ts +1 -20
  71. package/src/utils/types.ts +5 -0
  72. package/src/wrappers/ArraySet.ts +27 -14
  73. package/src/wrappers/ObjectMap.ts +1 -6
  74. package/src/wrappers/asMap.ts +1 -21
  75. package/src/wrappers/asSet.ts +0 -8
@@ -68,6 +68,19 @@ export function typesTuple<T extends AnyType[]>(...itemTypes: T): ArrayType<T> {
68
68
  // computeds cannot isolate tuple-item changes and only add allocation overhead.
69
69
  const checkTupleItems = createWholeContainerCachedCheck(checkTupleItemsImperatively)
70
70
 
71
+ const processorPlan = snapshotProcessorPlan(
72
+ () => checkers,
73
+ (processors) =>
74
+ processors.some(Boolean)
75
+ ? (array: unknown[]) => {
76
+ return array.map((item, i) => {
77
+ const processor = processors[i]
78
+ return processor ? withErrorPathSegment(i, () => processor(item)) : item
79
+ })
80
+ }
81
+ : undefined
82
+ )
83
+
71
84
  const thisTc: TypeChecker = new TypeChecker(
72
85
  TypeCheckerBaseType.Array,
73
86
 
@@ -102,31 +115,9 @@ export function typesTuple<T extends AnyType[]>(...itemTypes: T): ArrayType<T> {
102
115
  return thisTc
103
116
  },
104
117
 
105
- snapshotProcessorPlan(
106
- () => checkers,
107
- (processors) =>
108
- processors.some(Boolean)
109
- ? (array: unknown[]) => {
110
- return array.map((item, i) => {
111
- const processor = processors[i]
112
- return processor ? withErrorPathSegment(i, () => processor(item)) : item
113
- })
114
- }
115
- : undefined
116
- ),
117
-
118
- snapshotProcessorPlan(
119
- () => checkers,
120
- (processors) =>
121
- processors.some(Boolean)
122
- ? (array: unknown[]) => {
123
- return array.map((item, i) => {
124
- const processor = processors[i]
125
- return processor ? withErrorPathSegment(i, () => processor(item)) : item
126
- })
127
- }
128
- : undefined
129
- )
118
+ processorPlan,
119
+
120
+ processorPlan
130
121
  )
131
122
 
132
123
  return thisTc
@@ -19,6 +19,7 @@ import {
19
19
  } from "../tweaker/tweakedChangeListeners"
20
20
  import { getMobxVersion } from "../utils"
21
21
  import { ChunkedSequence, ChunkedSet, chunkSize, type SequenceChunk } from "../utils/chunks"
22
+ import { getOrCreate } from "../utils/mapUtils"
22
23
  import { TypeCheckError } from "./TypeCheckError"
23
24
 
24
25
  /**
@@ -79,11 +80,7 @@ function checkUncached(
79
80
  typeCheckedValue: any,
80
81
  check: () => TypeCheckError | null
81
82
  ): TypeCheckError | null {
82
- let results = uncachedResults!.get(checker)
83
- if (!results) {
84
- results = new Map()
85
- uncachedResults!.set(checker, results)
86
- }
83
+ const results = getOrCreate(uncachedResults!, checker, () => new Map())
87
84
  let error = results.get(value)
88
85
  if (error === undefined) {
89
86
  error = check()
@@ -152,11 +149,9 @@ export function createWholeContainerCachedCheck(
152
149
  )
153
150
  }
154
151
 
155
- let cached = cache.get(value)
156
- if (!cached) {
157
- cached = computed(() => check(value, emptyPath, undefined), { keepAlive: true })
158
- cache.set(value, cached)
159
- }
152
+ const cached = getOrCreate(cache, value, () =>
153
+ computed(() => check(value, emptyPath, undefined), { keepAlive: true })
154
+ )
160
155
  const error = cached.get()
161
156
  return contextualizeCachedError(error, path, typeCheckedValue)
162
157
  }
@@ -167,6 +162,10 @@ export function createWholeContainerCachedCheck(
167
162
  // check new chunks twice.
168
163
  const needsBatchToCacheChunks = getMobxVersion() < 6
169
164
 
165
+ function checkChunks<T>(fn: () => T): T {
166
+ return needsBatchToCacheChunks ? transaction(fn) : fn()
167
+ }
168
+
170
169
  /**
171
170
  * Checks the items of an array, starting at `firstIndex`, and returns the first
172
171
  * error, or null.
@@ -328,14 +327,8 @@ class RecordChunks
328
327
  readonly atom = createAtom("recordTypeCheckChunks")
329
328
  private readonly record: object
330
329
  private readonly checkKey: (key: string) => TypeCheckError | null
331
- private readonly getEntry = (key: string): CachedEntry => {
332
- let entry = this.entries.get(key)
333
- if (!entry) {
334
- entry = computed(() => this.checkKey(key))
335
- this.entries.set(key, entry)
336
- }
337
- return entry
338
- }
330
+ private readonly getEntry = (key: string): CachedEntry =>
331
+ getOrCreate(this.entries, key, () => computed(() => this.checkKey(key)))
339
332
 
340
333
  constructor(record: object, checkKey: (key: string) => TypeCheckError | null) {
341
334
  super()
@@ -458,10 +451,7 @@ export function createChunkedRecordCachedCheck(
458
451
  const chunks = cached?.chunks
459
452
  return checkUncached(cache, value, path, typeCheckedValue, () => {
460
453
  if (chunks && typeof key === "string") {
461
- const hasError = needsBatchToCacheChunks
462
- ? transaction(() => chunks.hasError(key))
463
- : chunks.hasError(key)
464
- if (!hasError) {
454
+ if (!checkChunks(() => chunks.hasError(key))) {
465
455
  return null
466
456
  }
467
457
  }
@@ -475,11 +465,10 @@ export function createChunkedRecordCachedCheck(
475
465
  addTweakedChangeListener<object>(value, chunks)
476
466
  const check = computed(
477
467
  () => {
478
- const hasError = needsBatchToCacheChunks
479
- ? transaction(() => chunks.hasError())
480
- : chunks.hasError()
481
468
  // any added or removed key changes a chunk, so this runs again then
482
- return hasError ? checkAllEntries(value, emptyPath, undefined) : null
469
+ return checkChunks(() => chunks.hasError())
470
+ ? checkAllEntries(value, emptyPath, undefined)
471
+ : null
483
472
  },
484
473
  { keepAlive: true }
485
474
  )
@@ -535,9 +524,7 @@ export function createChunkedArrayCachedCheck(
535
524
  if (!chunks || typeof index !== "number") {
536
525
  return checkItems(array, 0, emptyPath, undefined)
537
526
  }
538
- return needsBatchToCacheChunks
539
- ? transaction(() => chunks.check(index))
540
- : chunks.check(index)
527
+ return checkChunks(() => chunks.check(index))
541
528
  })
542
529
  }
543
530
 
@@ -561,7 +548,7 @@ export function createChunkedArrayCachedCheck(
561
548
  }
562
549
 
563
550
  const chunks = state.chunks
564
- return needsBatchToCacheChunks ? transaction(() => chunks.check()) : chunks.check()
551
+ return checkChunks(() => chunks.check())
565
552
  },
566
553
  { keepAlive: true }
567
554
  ),
@@ -4,3 +4,4 @@ export * from "./TypeCheckError"
4
4
  export * from "./tProp"
5
5
  export { typeCheck } from "./typeCheck"
6
6
  export * from "./types"
7
+ export type { CodecFromEncoded } from "./utility/typesCodecCore"
@@ -90,6 +90,11 @@ export function typesDataModelData<M = never, K = M>(
90
90
 
91
91
  const resolvedDataTypeChecker = resolveTypeChecker(standardDataType)
92
92
 
93
+ const processorPlan = snapshotProcessorPlan(
94
+ () => [resolvedDataTypeChecker],
95
+ ([processor]) => processor
96
+ )
97
+
93
98
  const thisTc: TypeChecker = new TypeChecker(
94
99
  TypeCheckerBaseType.Object,
95
100
 
@@ -104,15 +109,9 @@ export function typesDataModelData<M = never, K = M>(
104
109
  return resolvedDataTypeChecker.snapshotType(value) ? thisTc : null
105
110
  },
106
111
 
107
- snapshotProcessorPlan(
108
- () => [resolvedDataTypeChecker],
109
- ([processor]) => processor
110
- ),
112
+ processorPlan,
111
113
 
112
- snapshotProcessorPlan(
113
- () => [resolvedDataTypeChecker],
114
- ([processor]) => processor
115
- )
114
+ processorPlan
116
115
  )
117
116
 
118
117
  return thisTc
@@ -173,6 +173,14 @@ function typesObjectHelper<S>(objFn: S, frozen: boolean, typeInfoGen: TypeInfoGe
173
173
  }
174
174
  )
175
175
 
176
+ const processorPlan = snapshotProcessorPlan(
177
+ () => schemaEntries.map((entry) => entry.getResolvedChecker()),
178
+ (processors) =>
179
+ processors.some(Boolean)
180
+ ? (obj: Record<string, unknown>) => applySnapshotProcessor(obj, processors)
181
+ : undefined
182
+ )
183
+
176
184
  const thisTc: TypeChecker = new TypeChecker(
177
185
  TypeCheckerBaseType.Object,
178
186
 
@@ -208,21 +216,9 @@ function typesObjectHelper<S>(objFn: S, frozen: boolean, typeInfoGen: TypeInfoGe
208
216
  return thisTc
209
217
  },
210
218
 
211
- snapshotProcessorPlan(
212
- () => schemaEntries.map((entry) => entry.getResolvedChecker()),
213
- (processors) =>
214
- processors.some(Boolean)
215
- ? (obj: Record<string, unknown>) => applySnapshotProcessor(obj, processors)
216
- : undefined
217
- ),
218
-
219
- snapshotProcessorPlan(
220
- () => schemaEntries.map((entry) => entry.getResolvedChecker()),
221
- (processors) =>
222
- processors.some(Boolean)
223
- ? (obj: Record<string, unknown>) => applySnapshotProcessor(obj, processors)
224
- : undefined
225
- )
219
+ processorPlan,
220
+
221
+ processorPlan
226
222
  )
227
223
 
228
224
  return thisTc
@@ -83,6 +83,14 @@ export function typesRecord<T extends AnyType>(valueType: T): RecordType<T> {
83
83
 
84
84
  const checkRecordValues = createChunkedRecordCachedCheck(iterateRecordEntries, checkRecordEntry)
85
85
 
86
+ const processorPlan = snapshotProcessorPlan(
87
+ () => [valueChecker],
88
+ ([processor]) =>
89
+ processor
90
+ ? (obj: Record<string, unknown>) => applySnapshotProcessor(obj, processor)
91
+ : undefined
92
+ )
93
+
86
94
  const thisTc: TypeChecker = new TypeChecker(
87
95
  TypeCheckerBaseType.Object,
88
96
 
@@ -126,21 +134,9 @@ export function typesRecord<T extends AnyType>(valueType: T): RecordType<T> {
126
134
  return thisTc
127
135
  },
128
136
 
129
- snapshotProcessorPlan(
130
- () => [valueChecker],
131
- ([processor]) =>
132
- processor
133
- ? (obj: Record<string, unknown>) => applySnapshotProcessor(obj, processor)
134
- : undefined
135
- ),
136
-
137
- snapshotProcessorPlan(
138
- () => [valueChecker],
139
- ([processor]) =>
140
- processor
141
- ? (obj: Record<string, unknown>) => applySnapshotProcessor(obj, processor)
142
- : undefined
143
- )
137
+ processorPlan,
138
+
139
+ processorPlan
144
140
  )
145
141
 
146
142
  return thisTc
@@ -1,14 +1,11 @@
1
- import type { O } from "ts-toolbelt"
2
1
  import type { AnyDataModel } from "../dataModel/BaseDataModel"
3
2
  import type { AnyModel } from "../model/BaseModel"
4
3
  import type { ModelClass } from "../modelShared/BaseModelShared"
5
4
  import type { SnapshotInOf, SnapshotOutOf } from "../snapshot/SnapshotOf"
6
- import type { IsOptionalValue } from "../utils/types"
5
+ import type { IsOptionalValue, SimplifyObject } from "../utils/types"
7
6
 
8
7
  // type schemas
9
8
 
10
- // infer is there just to cache type generation
11
-
12
9
  export interface Type<Name, Data> {
13
10
  /** @ignore */
14
11
  $$type: Name
@@ -38,21 +35,21 @@ export interface CodecType<
38
35
  }
39
36
 
40
37
  type ArrayData<S extends readonly unknown[]> = number extends S["length"]
41
- ? Array<TypeToData<S[number]> extends infer R ? R : never>
38
+ ? Array<TypeToData<S[number]>>
42
39
  : {
43
- [k in keyof S]: TypeToData<S[k]> extends infer R ? R : never
40
+ [k in keyof S]: TypeToData<S[k]>
44
41
  }
45
42
 
46
43
  type ArraySnapshotInData<S extends readonly unknown[]> = number extends S["length"]
47
- ? Array<TypeToSnapshotIn<S[number]> extends infer R ? R : never>
44
+ ? Array<TypeToSnapshotIn<S[number]>>
48
45
  : {
49
- [k in keyof S]: TypeToSnapshotIn<S[k]> extends infer R ? R : never
46
+ [k in keyof S]: TypeToSnapshotIn<S[k]>
50
47
  }
51
48
 
52
49
  type ArraySnapshotOutData<S extends readonly unknown[]> = number extends S["length"]
53
- ? Array<TypeToSnapshotOut<S[number]> extends infer R ? R : never>
50
+ ? Array<TypeToSnapshotOut<S[number]>>
54
51
  : {
55
- [k in keyof S]: TypeToSnapshotOut<S[k]> extends infer R ? R : never
52
+ [k in keyof S]: TypeToSnapshotOut<S[k]>
56
53
  }
57
54
 
58
55
  export interface ArrayType<S extends readonly unknown[]> extends Type<"array", ArrayData<S>> {}
@@ -74,37 +71,28 @@ export type UndefinablePropsNames<T> = {
74
71
  * Computes the optional keys for an object type schema.
75
72
  * @ignore
76
73
  */
77
- export type ObjectOptionalKeys<S> = UndefinablePropsNames<{
78
- [k in keyof S]: TypeToDataOpt<S[k]> extends infer R ? R : never
79
- }>
80
-
81
- type ObjectData<S> = O.Optional<
82
- { [k in keyof S]: TypeToData<S[k]> extends infer R ? R : never },
83
- ObjectOptionalKeys<S>
74
+ export type ObjectOptionalKeys<S> = {
75
+ [K in keyof S]: IsOptionalValue<TypeToDataOpt<S[K]>, K, never>
76
+ }[keyof S]
77
+
78
+ type ObjectData<S, OK = ObjectOptionalKeys<S>> = SimplifyObject<
79
+ { [k in keyof S as k extends OK ? never : k]: TypeToData<S[k]> } & {
80
+ [k in keyof S as k extends OK ? k : never]?: TypeToData<S[k]>
81
+ }
84
82
  >
85
83
 
86
- type ObjectSnapshotInData<S> = O.Optional<
87
- { [k in keyof S]: TypeToSnapshotIn<S[k]> extends infer R ? R : never },
88
- ObjectOptionalKeys<S>
84
+ type ObjectSnapshotInData<S, OK = ObjectOptionalKeys<S>> = SimplifyObject<
85
+ { [k in keyof S as k extends OK ? never : k]: TypeToSnapshotIn<S[k]> } & {
86
+ [k in keyof S as k extends OK ? k : never]?: TypeToSnapshotIn<S[k]>
87
+ }
89
88
  >
90
89
 
91
- type ObjectSnapshotOutData<S> = O.Optional<
92
- { [k in keyof S]: TypeToSnapshotOut<S[k]> extends infer R ? R : never },
93
- ObjectOptionalKeys<S>
90
+ type ObjectSnapshotOutData<S, OK = ObjectOptionalKeys<S>> = SimplifyObject<
91
+ { [k in keyof S as k extends OK ? never : k]: TypeToSnapshotOut<S[k]> } & {
92
+ [k in keyof S as k extends OK ? k : never]?: TypeToSnapshotOut<S[k]>
93
+ }
94
94
  >
95
95
 
96
- type RecordData<S> = {
97
- [k: string]: TypeToData<S> extends infer R ? R : never
98
- }
99
-
100
- type RecordSnapshotInData<S> = {
101
- [k: string]: TypeToSnapshotIn<S> extends infer D ? D : never
102
- }
103
-
104
- type RecordSnapshotOutData<S> = {
105
- [k: string]: TypeToSnapshotOut<S> extends infer D ? D : never
106
- }
107
-
108
96
  export interface ObjectType<S> extends Type<"object", ObjectData<S>> {}
109
97
 
110
98
  export interface ObjectTypeFunction {
@@ -112,7 +100,9 @@ export interface ObjectTypeFunction {
112
100
  (): ObjectOfTypes
113
101
  }
114
102
 
115
- export interface RecordType<S> extends Type<"record", RecordData<S>> {}
103
+ // the record data types are written inline (instead of through an alias) so they are displayed as
104
+ // plain index signatures (e.g. `{ [k: string]: number }`)
105
+ export interface RecordType<S> extends Type<"record", { [k: string]: TypeToData<S> }> {}
116
106
 
117
107
  export type AnyStandardType =
118
108
  | IdentityType<any>
@@ -162,47 +152,45 @@ type TypeToSnapshotLeaf<S, Dir extends "in" | "out"> =
162
152
  ? Dir extends "in"
163
153
  ? SnapshotInOf<M>
164
154
  : SnapshotOutOf<M>
165
- : S extends { $$data: infer D }
166
- ? D
167
- : ConstructorToSnapshotData<S>
155
+ : ConstructorToSnapshotData<S>
168
156
 
169
157
  export type TypeToData<S> = S extends ObjectTypeFunction
170
- ? ObjectType<ReturnType<S>>["$$data"] extends infer R
171
- ? R
172
- : never
158
+ ? ObjectType<ReturnType<S>>["$$data"]
173
159
  : TypeToDataLeaf<S>
174
160
 
161
+ // standard types are dispatched through their `$$type` discriminant (and codec / identity types
162
+ // through indexed accesses), which is cheaper than checking them against each type interface
175
163
  export type TypeToSnapshotIn<S> = S extends ObjectTypeFunction
176
- ? ObjectSnapshotInData<ReturnType<S>> extends infer R
177
- ? R
178
- : never
179
- : S extends CodecType<any, infer D, any>
180
- ? D
181
- : S extends ArrayType<infer A>
182
- ? ArraySnapshotInData<A>
183
- : S extends ObjectType<infer O>
184
- ? ObjectSnapshotInData<O>
185
- : S extends RecordType<infer R>
186
- ? RecordSnapshotInData<R>
187
- : S extends ModelType<infer M>
188
- ? SnapshotInOf<M>
189
- : TypeToSnapshotLeaf<S, "in">
164
+ ? ObjectSnapshotInData<ReturnType<S>>
165
+ : S extends { $$type: infer K }
166
+ ? K extends "codec"
167
+ ? S["$$snapshotIn" & keyof S]
168
+ : S extends ArrayType<infer A>
169
+ ? ArraySnapshotInData<A>
170
+ : S extends ObjectType<infer O>
171
+ ? ObjectSnapshotInData<O>
172
+ : S extends RecordType<infer R>
173
+ ? { [k: string]: TypeToSnapshotIn<R> }
174
+ : S extends ModelType<infer M>
175
+ ? SnapshotInOf<M>
176
+ : S["$$data" & keyof S]
177
+ : TypeToSnapshotLeaf<S, "in">
190
178
 
191
179
  export type TypeToSnapshotOut<S> = S extends ObjectTypeFunction
192
- ? ObjectSnapshotOutData<ReturnType<S>> extends infer R
193
- ? R
194
- : never
195
- : S extends CodecType<any, any, infer D>
196
- ? D
197
- : S extends ArrayType<infer A>
198
- ? ArraySnapshotOutData<A>
199
- : S extends ObjectType<infer O>
200
- ? ObjectSnapshotOutData<O>
201
- : S extends RecordType<infer R>
202
- ? RecordSnapshotOutData<R>
203
- : S extends ModelType<infer M>
204
- ? SnapshotOutOf<M>
205
- : TypeToSnapshotLeaf<S, "out">
180
+ ? ObjectSnapshotOutData<ReturnType<S>>
181
+ : S extends { $$type: infer K }
182
+ ? K extends "codec"
183
+ ? S["$$snapshotOut" & keyof S]
184
+ : S extends ArrayType<infer A>
185
+ ? ArraySnapshotOutData<A>
186
+ : S extends ObjectType<infer O>
187
+ ? ObjectSnapshotOutData<O>
188
+ : S extends RecordType<infer R>
189
+ ? { [k: string]: TypeToSnapshotOut<R> }
190
+ : S extends ModelType<infer M>
191
+ ? SnapshotOutOf<M>
192
+ : S["$$data" & keyof S]
193
+ : TypeToSnapshotLeaf<S, "out">
206
194
 
207
195
  /** @ignore */
208
- export type TypeToDataOpt<S> = S extends { $$data: infer D } ? D & undefined : never
196
+ export type TypeToDataOpt<S> = S extends { $$data: unknown } ? S["$$data"] & undefined : never
@@ -6,6 +6,7 @@ import {
6
6
  toFullModelPropTransform,
7
7
  } from "../modelShared/prop"
8
8
  import { lazy } from "../utils"
9
+ import { getOrCreate } from "../utils/mapUtils"
9
10
  import {
10
11
  typesBoolean,
11
12
  typesNull,
@@ -24,26 +25,16 @@ const noDefaultValueSymbol = Symbol("noDefaultValue")
24
25
 
25
26
  const tPropCache = new WeakMap<AnyStandardType, Map<unknown, AnyModelProp>>()
26
27
 
27
- function createFromSnapshotProcessorGetter(
28
+ function createSnapshotProcessorGetter(
28
29
  getType: () => AnyStandardType,
29
- eagerType: AnyStandardType | undefined
30
+ eagerType: AnyStandardType | undefined,
31
+ getProcessor: (typeChecker: TypeChecker) => SnapshotProcessor | undefined
30
32
  ): (() => SnapshotProcessor | undefined) | undefined {
31
33
  if (eagerType instanceof TypeChecker) {
32
- const processor = eagerType.getFromSnapshotProcessor()
34
+ const processor = getProcessor(eagerType)
33
35
  return processor ? () => processor : undefined
34
36
  }
35
- return lazy(() => resolveTypeChecker(getType()).getFromSnapshotProcessor())
36
- }
37
-
38
- function createToSnapshotProcessorGetter(
39
- getType: () => AnyStandardType,
40
- eagerType: AnyStandardType | undefined
41
- ): (() => SnapshotProcessor | undefined) | undefined {
42
- if (eagerType instanceof TypeChecker) {
43
- const processor = eagerType.getToSnapshotProcessor()
44
- return processor ? () => processor : undefined
45
- }
46
- return lazy(() => resolveTypeChecker(getType()).getToSnapshotProcessor())
37
+ return lazy(() => getProcessor(resolveTypeChecker(getType())))
47
38
  }
48
39
 
49
40
  type AnyTypeOrArray = AnyType | ReadonlyArray<AnyType>
@@ -53,12 +44,22 @@ type StoredRequiredPropMetadata<TType extends AnyType> = {
53
44
  $storedCreationValueType: TypeToStoredData<TType>
54
45
  }
55
46
 
56
- type TypedMaybeOptionalModelProp<TType extends AnyType> = MaybeOptionalModelProp<
47
+ // exported (and publicly re-exported) so emitted declaration files can reference it by name
48
+ // instead of expanding it inline in every exported model class, which is much slower and larger
49
+ /**
50
+ * A model property created by `tProp` without a default value.
51
+ */
52
+ export type TypedMaybeOptionalModelProp<TType extends AnyType> = MaybeOptionalModelProp<
57
53
  TypeToData<TType>
58
54
  > &
59
55
  StoredRequiredPropMetadata<TType>
60
56
 
61
- type TypedOptionalModelProp<TType extends AnyType> = OptionalModelProp<TypeToData<TType>> & {
57
+ // exported (and publicly re-exported) so emitted declaration files can reference it by name
58
+ // instead of expanding it inline in every exported model class, which is much slower and larger
59
+ /**
60
+ * A model property created by `tProp` with a default value.
61
+ */
62
+ export type TypedOptionalModelProp<TType extends AnyType> = OptionalModelProp<TypeToData<TType>> & {
62
63
  $storedValueType: TypeToStoredData<TType>
63
64
  $storedCreationValueType: TypeToStoredData<TType> | null | undefined
64
65
  $typedFromSnapshotOverride: TypeToSnapshotIn<TType> | null | undefined
@@ -124,7 +125,7 @@ export function tProp(defaultValue: boolean): TypedOptionalModelProp<typeof type
124
125
  */
125
126
  export function tProp<TType extends AnyType>(
126
127
  type: TType,
127
- defaultFn: () => TypeToData<TType>
128
+ defaultFn: () => NoInfer<TypeToData<TType>>
128
129
  ): TypedOptionalModelProp<TType>
129
130
 
130
131
  /**
@@ -145,7 +146,7 @@ export function tProp<TType extends AnyType>(
145
146
  */
146
147
  export function tProp<TType extends ReadonlyArray<AnyType>>(
147
148
  type: TType,
148
- defaultFn: () => TypeToData<TType[number]>
149
+ defaultFn: () => NoInfer<TypeToData<TType[number]>>
149
150
  ): TypedOptionalModelProp<TType[number]>
150
151
 
151
152
  /**
@@ -167,7 +168,7 @@ export function tProp<TType extends ReadonlyArray<AnyType>>(
167
168
  */
168
169
  export function tProp<TType extends AnyType>(
169
170
  type: TType,
170
- defaultValue: TypeToData<TType>
171
+ defaultValue: NoInfer<TypeToData<TType>>
171
172
  ): TypedOptionalModelProp<TType>
172
173
 
173
174
  /**
@@ -188,7 +189,7 @@ export function tProp<TType extends AnyType>(
188
189
  */
189
190
  export function tProp<TType extends ReadonlyArray<AnyType>>(
190
191
  type: TType,
191
- defaultValue: TypeToData<TType[number]>
192
+ defaultValue: NoInfer<TypeToData<TType[number]>>
192
193
  ): TypedOptionalModelProp<TType[number]>
193
194
 
194
195
  /**
@@ -250,11 +251,7 @@ export function tProp(
250
251
  const typeChecker = resolveStandardType(resolvedType)
251
252
  const defKey = hasDefaultValue ? def : noDefaultValueSymbol
252
253
 
253
- let defValueCache = tPropCache.get(typeChecker)
254
- if (!defValueCache) {
255
- defValueCache = new Map()
256
- tPropCache.set(typeChecker, defValueCache)
257
- }
254
+ const defValueCache = getOrCreate(tPropCache, typeChecker, () => new Map())
258
255
 
259
256
  const cachedProp = defValueCache.get(defKey)
260
257
  if (cachedProp) {
@@ -301,11 +298,14 @@ export function tProp(
301
298
  ? toFullModelPropTransform(createCodecPropTransform(typeChecker))
302
299
  : undefined
303
300
  ),
304
- _getFromSnapshotProcessor: createFromSnapshotProcessorGetter(
301
+ _getFromSnapshotProcessor: createSnapshotProcessorGetter(
305
302
  getFromSnapshotType,
306
- eagerFromSnapshotType
303
+ eagerFromSnapshotType,
304
+ (tc) => tc.getFromSnapshotProcessor()
305
+ ),
306
+ _getToSnapshotProcessor: createSnapshotProcessorGetter(getStoredType, eagerStoredType, (tc) =>
307
+ tc.getToSnapshotProcessor()
307
308
  ),
308
- _getToSnapshotProcessor: createToSnapshotProcessorGetter(getStoredType, eagerStoredType),
309
309
  } satisfies Partial<AnyModelProp>)
310
310
 
311
311
  defValueCache.set(defKey, newProp)
@@ -1,7 +1,6 @@
1
- import type { O } from "ts-toolbelt"
1
+ import type { SimplifyObject } from "../../utils/types"
2
2
  import type {
3
3
  ArrayType,
4
- CodecType,
5
4
  ObjectOptionalKeys,
6
5
  ObjectType,
7
6
  ObjectTypeFunction,
@@ -10,28 +9,30 @@ import type {
10
9
  } from "../schemas"
11
10
 
12
11
  type ArrayStoredData<S extends readonly unknown[]> = number extends S["length"]
13
- ? Array<TypeToStoredData<S[number]> extends infer R ? R : never>
12
+ ? Array<TypeToStoredData<S[number]>>
14
13
  : {
15
- [k in keyof S]: TypeToStoredData<S[k]> extends infer R ? R : never
14
+ [k in keyof S]: TypeToStoredData<S[k]>
16
15
  }
17
16
 
18
- type ObjectStoredData<S> = O.Optional<
19
- { [k in keyof S]: TypeToStoredData<S[k]> extends infer R ? R : never },
20
- ObjectOptionalKeys<S>
17
+ type ObjectStoredData<S, OK = ObjectOptionalKeys<S>> = SimplifyObject<
18
+ { [k in keyof S as k extends OK ? never : k]: TypeToStoredData<S[k]> } & {
19
+ [k in keyof S as k extends OK ? k : never]?: TypeToStoredData<S[k]>
20
+ }
21
21
  >
22
22
 
23
+ // standard types are dispatched through their `$$type` discriminant (see TypeToSnapshotIn)
23
24
  export type TypeToStoredData<S> = S extends ObjectTypeFunction
24
- ? ObjectStoredData<ReturnType<S>> extends infer R
25
- ? R
26
- : never
27
- : S extends CodecType<any, any, any, infer D>
28
- ? D
29
- : S extends ArrayType<infer A>
30
- ? ArrayStoredData<A>
31
- : S extends ObjectType<infer O>
32
- ? ObjectStoredData<O>
33
- : S extends RecordType<infer R>
34
- ? {
35
- [k: string]: TypeToStoredData<R> extends infer D ? D : never
36
- }
37
- : TypeToData<S>
25
+ ? ObjectStoredData<ReturnType<S>>
26
+ : S extends { $$type: infer K }
27
+ ? K extends "codec"
28
+ ? S["$$storedData" & keyof S]
29
+ : S extends ArrayType<infer A>
30
+ ? ArrayStoredData<A>
31
+ : S extends ObjectType<infer O>
32
+ ? ObjectStoredData<O>
33
+ : S extends RecordType<infer R>
34
+ ? {
35
+ [k: string]: TypeToStoredData<R>
36
+ }
37
+ : TypeToData<S>
38
+ : TypeToData<S>