Object
Migration Guide
Step-by-step guide to migrating from Lodash or vanilla JavaScript object operations to Receta's Object module.
Why Migrate?
Before (Lodash):
- Manual immutability with
cloneDeep - No type safety
- Not composable in pipelines
- No Result/Option integration
After (Receta):
- Immutable by default
- Full TypeScript support
- Pipeline-friendly
- Integrated error handling
Migration Steps
Step 1: Replace Deep Get/Set
// ❌ Lodash
import _ from 'lodash'
const value = _.get(obj, 'deeply.nested.value', 'default')
_.set(_.cloneDeep(obj), 'deeply.nested.value', 'new')
// ✅ Receta
import { getPath, setPath } from '@ontologyio/receta/object'
import { unwrapOr } from '@ontologyio/receta/option'
const value = pipe(
obj,
getPath<string>(['deeply', 'nested', 'value']),
unwrapOr('default')
)
const updated = setPath(['deeply', 'nested', 'value'], 'new', obj)Step 2: Replace Pick/Omit with Mask
// ❌ Lodash
const safe = _.pick(user, ['id', 'email'])
// ✅ Receta (security-focused)
const safe = mask(user, ['id', 'email'])Step 3: Replace mapKeys/mapValues
// ❌ Lodash
_.mapKeys(obj, (value, key) => key.toUpperCase())
_.mapValues(obj, (value) => value * 2)
// ✅ Receta (mapKeys passes the key first)
mapKeys(obj, (key, value) => key.toUpperCase())
mapValues(obj, (value) => value * 2)Step 4: Replace Deep Merge
// ❌ Lodash
_.merge(_.cloneDeep(obj1), obj2, obj3)
// ✅ Receta
deepMerge([obj1, obj2, obj3])Step 5: Add Validation
import { flatMap, fromResult } from '@ontologyio/receta/option'
// ❌ Lodash (no validation)
const config = _.get(data, 'config', {})
// Hope it's the right shape!
// ✅ Receta (with validation)
const config = pipe(
data,
getPath(['config']),
flatMap((c) => fromResult(validateShape(c, configSchema))),
unwrapOr(defaultConfig)
)Common Patterns
Pattern: Nested Updates
// ❌ Before
const renameUser = (state: State) => {
const updated = _.cloneDeep(state)
_.set(updated, 'user.profile.name', 'Alice')
return updated
}
// ✅ After
const renameUser = (state: State) =>
setPath(['user', 'profile', 'name'], 'Alice', state)Pattern: Safe Access
import { tap } from '@ontologyio/receta/option'
// ❌ Before
const email = _.get(user, 'profile.contact.email')
if (email) {
sendEmail(email)
}
// ✅ After
pipe(
user,
getPath<string>(['profile', 'contact', 'email']),
tap(sendEmail)
)Pattern: Transformation Pipeline
// ❌ Before
let result = _.cloneDeep(apiResponse)
result = _.mapKeys(result, (v, k) => _.camelCase(k))
result = _.omit(result, ['_internal', '_cache'])
result = _.mapValues(result, (v) => typeof v === 'string' ? v.trim() : v)
// ✅ After
const result = pipe(
apiResponse,
filterKeys((key) => !key.startsWith('_')),
transformKeys('camelCase', { deep: false }),
mapValues((v) => typeof v === 'string' ? v.trim() : v)
)Migration Checklist
- Replace
_.get→getPath+unwrapOr - Replace
_.set→setPath(removecloneDeep) - Replace
_.pick→mask(when security-focused) - Replace
_.mapKeys→mapKeys - Replace
_.mapValues→mapValues - Replace
_.merge→deepMerge(removecloneDeep) - Add validation with
validateShape - Add type annotations
- Convert to pipelines where appropriate
Performance Notes
- No
cloneDeepneeded: only the objects along a path are copied - Tree-shakeable = smaller bundles
- Type-safe = fewer runtime errors
Next Steps
- API Reference - Complete function reference
- Patterns - More real-world examples