Created
December 3, 2025 16:52
-
-
Save zach2825/bb29e6bfc665e86f9432de022c9e90ff to your computer and use it in GitHub Desktop.
A minimal BaseModel in TypeScript that tracks field-level changes and generates diff payloads.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { isEqual, cloneDeep, set, get } from 'lodash' | |
| // === BaseModel Class === | |
| class BaseModel { | |
| static _original: Record<string, any> = {} | |
| static _changes: Record<string, any> = {} | |
| id: string | |
| constructor(data: Partial<any> = {}) { | |
| Object.assign(this, data) | |
| this.id = data.id || Math.random().toString(36).substring(2) | |
| this.$initChangesTracking() | |
| } | |
| $initChangesTracking() { | |
| const Ctor = this.constructor as typeof BaseModel | |
| Ctor._original[this.id] = cloneDeep(this) | |
| Ctor._changes[this.id] = {} | |
| } | |
| trackChange(key: string, value: any) { | |
| const Ctor = this.constructor as typeof BaseModel | |
| const originalValue = get(Ctor._original[this.id], key) | |
| const isReverted = isEqual(originalValue, value) | |
| if (isReverted) { | |
| delete Ctor._changes[this.id][key] | |
| } else { | |
| set(Ctor._changes[this.id], key, value) | |
| } | |
| set(this, key, value) | |
| } | |
| get $changeRegistry(): Record<string, any> { | |
| const Ctor = this.constructor as typeof BaseModel | |
| const changes = Ctor._changes[this.id] || {} | |
| const original = Ctor._original[this.id] || {} | |
| const result: Record<string, any> = {} | |
| for (const key in changes) { | |
| const current = get(this, key) | |
| const originalValue = get(original, key) | |
| if (!isEqual(current, originalValue)) { | |
| result[key] = current | |
| } | |
| } | |
| return result | |
| } | |
| // Example $update override that uses trackChange | |
| $update(payload: Record<string, any>) { | |
| for (const [key, value] of Object.entries(payload)) { | |
| this.trackChange(key, value) | |
| } | |
| } | |
| } | |
| // === User Class (Example Model) === | |
| class User extends BaseModel { | |
| name = '' | |
| email = '' | |
| constructor(data: Partial<User> = {}) { | |
| super(data) | |
| Object.assign(this, data) | |
| } | |
| } | |
| // === Usage Example === | |
| const user = new User({ id: 'u1', name: 'Alice', email: '[email protected]' }) | |
| console.log('Initial:', user) | |
| user.$update({ name: 'Alicia' }) | |
| console.log('After name change:', user.$changeRegistry) | |
| user.$update({ name: 'Alice' }) // revert | |
| console.log('After reverting name:', user.$changeRegistry) | |
| user.$update({ email: '[email protected]' }) | |
| console.log('Final patch payload to send:', user.$changeRegistry) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment