Property Transforms and Collection Models (deprecated)
Property transforms (.withTransform(...)), the built-in transform helpers and the ObjectMap / ArraySet collection models still work but are deprecated. Use codecs instead; each section below lists the alternative.
Property transforms (.withTransform(...))
Alternative: Use tProp(types.codec(...)) or a built-in codec type. Wrap with types.skipCheck(...) if you don't need runtime validation.
Property transforms are applied per-property on top of a plain prop(...) declaration. They convert between the stored value and the value exposed on the model instance.
Compared to codecs, property transforms:
- Don't integrate with the type system (no
typeCheck(...), no typedfromSnapshot/getSnapshot) - Don't compose — each property declares its own transform
- Don't support nested conversion (e.g. a
Map<string, Date>) - Require you to manually declare snapshot types via
prop<StoredType>()
// ❌ Deprecated
@model("MyApp/M")
class M extends Model({
date: prop<number>().withTransform(timestampToDateTransform()).withSetter(),
}) {}
// ✅ Preferred
@model("MyApp/M")
class M extends Model({
date: tProp(types.dateAsTimestamp).withSetter(),
// or without runtime checking:
// date: tProp(types.skipCheck(types.dateAsTimestamp)).withSetter(),
}) {}
Creating a custom property transform
If you have a one-off conversion that doesn't warrant a full codec, you can still create a custom property transform:
// ModelPropTransform<TOriginalValue, TTransformedValue>
const _timestampToDateTransform: ModelPropTransform<number, Date> = {
transform({ originalValue, cachedTransformedValue, setOriginalValue }) {
return cachedTransformedValue ?? new ImmutableDate(originalValue)
},
untransform({ transformedValue, cacheTransformedValue }) {
if (transformedValue instanceof ImmutableDate) {
cacheTransformedValue()
}
return +transformedValue
},
}
export const timestampToDateTransform = () => _timestampToDateTransform
However, for reusable conversions, prefer types.codec(...) — see Custom codecs.
Built-in transform helpers
All built-in transform helpers are deprecated. Use the corresponding codec instead:
| Deprecated transform | Codec alternative |
|---|---|
timestampToDateTransform() | types.dateAsTimestamp |
isoStringToDateTransform() | types.dateAsIsoString |
stringToBigIntTransform() | types.bigint |
objectToMapTransform() | types.mapFromObject(valueType) |
arrayToMapTransform() | types.mapFromArray(keyType, valueType) |
arrayToSetTransform() | types.setFromArray(valueType) |
Codecs additionally support nested conversion. For example, types.mapFromArray(types.dateAsIsoString, types.bigint) converts both keys and values, which is not possible with arrayToMapTransform().
If you want the codec conversion without runtime type-checking overhead, wrap with types.skipCheck(...):
tProp(types.skipCheck(types.dateAsTimestamp))
// equivalent to: prop<number>().withTransform(timestampToDateTransform())
// but with proper snapshot typing and composability
Collection models (ObjectMap / ArraySet)
Alternative: Use tProp(types.mapFromObject(...)), tProp(types.mapFromArray(...)), or tProp(types.setFromArray(...)).
ObjectMap and ArraySet are special model wrappers that provide Map-like and Set-like interfaces. They produce snapshots that include $modelType and $modelId metadata, whereas codecs produce clean plain objects/arrays.
Input snapshot types for these collections use SnapshotInOf for their items, so defaulted model fields may be omitted. Output snapshots use SnapshotOutOf for their items. Collection snapshots still require their $modelId.
ObjectMap collection model
Reading entries through forEach inside a MobX reaction tracks entry additions, removals, and value changes, including on MobX 4.
// ❌ Deprecated
class LegacyObjectMapStore extends Model({
myNumberMap: prop(() => objectMap<number>())
}) {}
// or without a default:
// class LegacyObjectMapStore extends Model({
// myNumberMap: prop<ObjectMap<number>>()
// }) {}
// ✅ Preferred
class MapFromObjectStore extends Model({
myNumberMap: tProp(types.mapFromObject(types.number), () => new Map())
}) {}
Snapshot representation of ObjectMap (includes model metadata):
{
$modelType: "mobx-keystone/ObjectMap",
$modelId: "Td244...",
items: {
"key1": value1,
"key2": value2,
}
}
ArraySet collection model
// ❌ Deprecated
class LegacyArraySetStore extends Model({
myNumberSet: prop(() => arraySet<number>())
}) {}
// or without a default:
// class LegacyArraySetStore extends Model({
// myNumberSet: prop<ArraySet<number>>()
// }) {}
// ✅ Preferred
class SetFromArrayStore extends Model({
myNumberSet: tProp(types.setFromArray(types.number), () => new Set())
}) {}
Snapshot representation of ArraySet (includes model metadata):
{
$modelType: "mobx-keystone/ArraySet",
$modelId: "Td244...",
items: [
value1,
value2
]
}