Skip to main content

Property Transforms and Collection Models (deprecated)

warning

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 typed fromSnapshot/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 transformCodec alternative
timestampToDateTransform()types.dateAsTimestamp
isoStringToDateTransform()types.dateAsIsoString
stringToBigIntTransform()types.bigint
objectToMapTransform()types.mapFromObject(valueType)
arrayToMapTransform()types.mapFromArray(keyType, valueType)
arrayToSetTransform()types.setFromArray(valueType)
tip

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
]
}