Skip to content

quickmodel / QModel

Abstract Class: QModel<TInterface, TAliasMap, TSensitiveKeys> ​

Type Parameters ​

TInterface ​

TInterface extends IQAnyRecord

TAliasMap ​

TAliasMap extends Record<string, string> = Record<never, never>

TSensitiveKeys ​

TSensitiveKeys extends keyof TInterface = never

Accessors ​

$qHistory ​

Get Signature ​

get $qHistory(): IQHistoryHandle

Returns the history tracking handle for this instance.

When history is enabled ({ history: { enabled: true } }), returns an IQHistoryHandle that records one entry per operation (or per field when recordMode: 'field') with shape { method, at, changes: { field: { from, to } } }.

When history is disabled (the default), returns a shared no-op handle whose value is always [] and all methods are no-ops.

Example ​
typescript
@Quick({ name: String }, { history: { enabled: true } })
class Contract extends QModel<IContract> { declare name: string; }

const c = new Contract({ name: 'v1' });
c.$qPatch({ name: 'v2' });
c.$qHistory.value;
// [{ method: 'patch', at: Date, changes: { name: { from: 'v1', to: 'v2' } } }]
See ​
Returns ​

IQHistoryHandle


$qSignal ​

Get Signature ​

get $qSignal(): IQModelSignal<this>

The built-in reactive signal for this model instance.

Provides the minimal contract (peek(), version, subscribe()) needed to bridge with any framework's reactive system without adding any framework dependency to QuickModel.

The version counter increments automatically on every property change, even before any subscriber is registered.

The signal object is created lazily on first access and then cached — repeated accesses always return the same instance.

See ​

IQModelSignal — the public interface type

Examples ​

Angular 17+

typescript
// adapter (5 lines, no framework dependency in QuickModel)
const sig = signal(model);
model.$qSignal.subscribe(() => sig.set(model));
return sig.asReadonly();

React

typescript
useSyncExternalStore(
  (cb) => model.$qSignal.subscribe(cb as any),
  () => model.$qSerialize(),
);
Returns ​

IQModelSignal<this>

Constructors ​

Constructor ​

new QModel<TInterface, TAliasMap, TSensitiveKeys>(data): QModel<TInterface, TAliasMap, TSensitiveKeys>

Constructs a new model instance from interface data or another instance. Automatically deserializes complex types (Date, BigInt, etc.) based on @QType decorators.

new Model(data) and Model.create(data) are exactly equivalent — create is a typed wrapper that adds stricter inference at the call-site. Both respect unknownPropertyPolicy and all @Quick options.

Parameters ​

data ​

IQAliasInput<TInterface, TAliasMap> | IQModelData<TInterface> | QModel<TInterface, Record<never, never>, never>

Either a plain interface object or another model instance (for cloning)

Returns ​

QModel<TInterface, TAliasMap, TSensitiveKeys>

See ​

QModel.create — preferred factory alias with stricter type inference

Example ​

typescript
// From interface data
const user = new User({
  id: '1',
  name: 'John',
  createdAt: new Date() // or '2024-01-01T00:00:00.000Z'
});

// Equivalent using the factory:
const user2 = User.create({ id: '2', name: 'Jane', createdAt: new Date() });

// Clone from another instance
const clonedUser = new User(user);

Methods ​

$qCheckFieldAsync() ​

$qCheckFieldAsync(fieldName, options?): Promise<IQRulesResult>

Evaluates only the @QRule predicates attached to a single field asynchronously.

Equivalent to $qCheckRulesAsync({ field }) but available directly on the instance for ergonomic step-by-step form validation — validate one input at a time without re-running the entire model.

Supports all async options: timeoutMs, signal, globalTimeoutMs, retry, etc.

Parameters ​

fieldName ​

string

The property name whose @QRule decorators to evaluate.

options? ​

IQRulesAsyncOptions

Optional async execution options (same as $qCheckRulesAsync).

Returns ​

Promise<IQRulesResult>

Promise<IQRulesResult> — valid: true when all rules for this field pass.

Example ​

typescript
// Validate only the 'email' field on input change:
const result = await form.$qCheckFieldAsync('email');
if (!result.valid) {
  showErrors(result.errors);
}

// With per-predicate timeout:
const result = await form.$qCheckFieldAsync('username', { timeoutMs: 500 });

See ​


$qCheckIntegrity() ​

$qCheckIntegrity(): IQIntegrityResult[]

Checks the model instance for type integrity.

Returns ​

IQIntegrityResult[]

Array of integrity errors (empty if all pass)


$qCheckRules() ​

$qCheckRules(): IQRulesResult

Evaluates all @QRule business-logic rules synchronously.

Returns ​

IQRulesResult


$qCheckRulesAsync() ​

$qCheckRulesAsync(options?): Promise<IQRulesResult>

Async version of $qCheckRules().

Parameters ​

options? ​

IQRulesAsyncOptions

Returns ​

Promise<IQRulesResult>


$qCopy() ​

$qCopy(partial?): this

Returns a new instance that is an immutable copy of the current state.

Parameters ​

partial? ​

Partial<IQModelData<TInterface>>

Returns ​

this


$qDiff() ​

$qDiff(other): Record<string, { after: unknown; before: unknown; }>

Compares this instance with another and returns a field-by-field diff.

Parameters ​

other ​

this

Returns ​

Record<string, { after: unknown; before: unknown; }>


$qEquals() ​

$qEquals(other): boolean

Returns true when this instance is deeply equal to other.

Parameters ​

other ​

this

Returns ​

boolean


$qFrom() ​

$qFrom(data): this

Creates a new instance of this model's class from a plain object.

Instance-level counterpart of the static QModel.deserialize method. Useful when you have a model instance and need to construct a sibling from raw data without referencing the class name explicitly.

Parameters ​

data ​

IQModelData<TInterface>

Plain object matching the model's interface

Returns ​

this

A new instance of the same model class

See ​

Example ​

typescript
const user = new User({ id: 1, name: 'Alice' });
const other = user.$qFrom({ id: 2, name: 'Bob' });
other instanceof User; // true

$qFromJSON() ​

$qFromJSON(json): this

Creates a new instance of this model's class from a JSON string.

Instance-level counterpart of the static QModel.fromJSON method. Useful when you have a model instance and need to reconstruct a sibling from a serialized JSON string without referencing the class name explicitly.

Parameters ​

json ​

string

JSON string produced by $qToJSON() or any compatible source

Returns ​

this

A new instance of the same model class

Throws ​

If json is not valid JSON

See ​

Example ​

typescript
const json = user.$qToJSON();
const restored = user.$qFromJSON(json);
restored instanceof User;           // true
restored.createdAt instanceof Date; // true

$qGetChangedFields() ​

$qGetChangedFields(): string[]

Returns an array of field names that have changed since construction.

Returns ​

string[]


$qGetChanges() ​

$qGetChanges(): Partial<IQSerializedInterface<TInterface>>

Returns an object containing only the fields that have changed.

Returns ​

Partial<IQSerializedInterface<TInterface>>


$qGetDirtyFields() ​

$qGetDirtyFields(): Set<string>

Returns a Set of field names that have changed since construction.

Returns ​

Set<string>


$qGetFormSchema() ​

$qGetFormSchema(): IQFormSchemaEntry[]

Returns the form schema for this instance, built from @QField decorators.

Returns ​

IQFormSchemaEntry[]

Ordered array of IQFormSchemaEntry — one per @QField-decorated property.

See ​


$qGetFormSchemaGrouped() ​

$qGetFormSchemaGrouped(): IQFormSchemaGroup[]

Returns the form schema grouped by @QGroup sections.

Returns ​

IQFormSchemaGroup[]

Ordered array of { group, fields } entries — see IQFormSchemaGroup.

See ​


$qGetInitInterface() ​

$qGetInitInterface(): IQSerializedInterface<TInterface>

Returns the initial state as passed to the constructor.

Returns ​

IQSerializedInterface<TInterface>


$qGetMetadata() ​

$qGetMetadata(): Map<string, { transformer: unknown; type: string; }>

Instance alias for static getMetadata. Useful for inspecting model state and configuration from an instance.

Includes Dynamic Auto-discovery: Returns both statically defined fields AND instance-specific fields found on this object (e.g. declare properties without decorators).

Returns ​

Map<string, { transformer: unknown; type: string; }>

See ​

QModel.getMetadata


$qGetSchema() ​

$qGetSchema<T>(type): IQSchemaReturnType<T>

Generate schema with examples from instance values.

For JSON/OpenAPI schemas, adds example fields with actual values from the instance.

Type Parameters ​

T ​

T extends IQSchemaType

Parameters ​

type ​

T

Schema type to generate

Returns ​

IQSchemaReturnType<T>

Generated schema with examples

See ​

QModel.getSchema — static variant (no examples, returns raw schema)

Example ​

Generate JSON Schema with examples

typescript
const user = new User({
  id: 42,
  name: 'John Doe',
  createdAt: '2024-01-01',
  balance: '999999'
});

const schema = user.$qGetSchema('json');
// {
//   ...
//   properties: {
//     id: { type: 'number', example: 42 },
//     name: { type: 'string', example: 'John Doe' },
//     createdAt: { type: 'string', format: 'date-time', example: '2024-01-01T00:00:00.000Z' },
//     balance: { type: 'string', pattern: '^-?\\d+$', example: '999999' }
//   }
// }

$qHasChanges() ​

$qHasChanges(): boolean

Returns true if any field has changed since construction.

Returns ​

boolean


$qHasIntegrity() ​

$qHasIntegrity(): boolean

Returns true when integrity check passes (no errors).

Returns ​

boolean


$qIsDirty() ​

$qIsDirty(field?): boolean

Checks if the model (or a specific field) has been modified since construction.

Parameters ​

field? ​

string

Returns ​

boolean


$qIsValid() ​

$qIsValid(): boolean

Returns true when both integrity and sync rules pass.

Returns ​

boolean


$qIsValidAsync() ​

$qIsValidAsync(options?): Promise<boolean>

Async version of $qIsValid().

Parameters ​

options? ​

IQRulesAsyncOptions

Returns ​

Promise<boolean>


$qPatch() ​

$qPatch(patch): void

Applies partial updates to the model.

Parameters ​

patch ​

Partial<IQModelData<TInterface>>

Returns ​

void


$qReset() ​

$qReset(): void

Resets the model to its initial state.

Returns ​

void


$qSerialize() ​

Call Signature ​

$qSerialize(options): IQAliasedSerializedInterface<TInterface, TAliasMap>

Serializes the model instance to a plain interface object.

Complex types (Date, BigInt, Map, Set, RegExp, …) are converted to JSON-serializable primitives according to each transformer's serialize() logic. @QAlias remapping is applied after serialization.

Parameters ​
options ​

IQSerializationOptions & object

Optional pick/omit field list to filter the result.

Returns ​

IQAliasedSerializedInterface<TInterface, TAliasMap>

The IQAliasedSerializedInterface snapshot with all complex types converted to primitives.

See ​

Call Signature ​

$qSerialize(seen, options): IQAliasedSerializedInterface<TInterface, TAliasMap>

Serializes the model instance to a plain interface object.

Complex types (Date, BigInt, Map, Set, RegExp, …) are converted to JSON-serializable primitives according to each transformer's serialize() logic. @QAlias remapping is applied after serialization.

Parameters ​
seen ​

WeakSet<object>

options ​

IQSerializationOptions & object

Optional pick/omit field list to filter the result.

Returns ​

IQAliasedSerializedInterface<TInterface, TAliasMap>

The IQAliasedSerializedInterface snapshot with all complex types converted to primitives.

See ​

Call Signature ​

$qSerialize(options?): IQSafeSerializedInterface<TInterface, TAliasMap, TSensitiveKeys>

Serializes the model instance to a plain interface object.

Complex types (Date, BigInt, Map, Set, RegExp, …) are converted to JSON-serializable primitives according to each transformer's serialize() logic. @QAlias remapping is applied after serialization.

Parameters ​
options? ​

IQSerializationOptions

Optional pick/omit field list to filter the result.

Returns ​

IQSafeSerializedInterface<TInterface, TAliasMap, TSensitiveKeys>

The IQAliasedSerializedInterface snapshot with all complex types converted to primitives.

See ​

Call Signature ​

$qSerialize(seen?, options?): IQSafeSerializedInterface<TInterface, TAliasMap, TSensitiveKeys>

Serializes the model instance to a plain interface object.

Complex types (Date, BigInt, Map, Set, RegExp, …) are converted to JSON-serializable primitives according to each transformer's serialize() logic. @QAlias remapping is applied after serialization.

Parameters ​
seen? ​

WeakSet<object>

options? ​

IQSerializationOptions

Optional pick/omit field list to filter the result.

Returns ​

IQSafeSerializedInterface<TInterface, TAliasMap, TSensitiveKeys>

The IQAliasedSerializedInterface snapshot with all complex types converted to primitives.

See ​

$qSubscribe() ​

$qSubscribe(callback): () => void

Registers an observer callback that fires every time any property on this model instance changes through the smart setter.

The callback receives an IQChange payload with the field name, the value before the change (already type-transformed), and the value after.

Observers are instance-scoped — they are NOT copied by $qCopy().

Parameters ​

callback ​

IQObserverFn

Returns ​

An unsubscribe function. Call it to deregister the observer.

() => void

Example ​

typescript
const user = new UserModel({ name: 'Alice', age: 30 });
const unsub = user.$qSubscribe(({ field, prev, next }) => {
  console.log(`${field}: ${String(prev)} → ${String(next)}`);
});
user.name = 'Bob'; // logs: "name: Alice → Bob"
unsub();           // stop receiving notifications

$qToFormData() ​

$qToFormData(options?): Promise<FormData>

Serializes the model to FormData.

Parameters ​

options? ​

IToFormDataOptions

Returns ​

Promise<FormData>

See ​

QModel.fromFormData — inverse: parse a FormData into a model instance


$qToInterface() ​

$qToInterface(seen?, depth?): TInterface

Converts the current state to interface format (preserving original input types).

Parameters ​

seen? ​

WeakSet<object>

depth? ​

number

Returns ​

TInterface


$qToJSON() ​

$qToJSON(options?): string

QuickModel-specific helper that serializes the model to a JSON string.

Combines $qSerialize() and JSON.stringify() in one call, with full support for QuickModel serialization options (@QAlias remapping, @QSensitive exclusion, omit, etc.).

This is NOT the JS toJSON protocol. For JSON.stringify(model) to work correctly, see toJSON() below.

SOLID - Single Responsibility: Delegates to Serializer service.

Parameters ​

options? ​

IQSerializationOptions

Returns ​

string

JSON string representation of the model

See ​

Example ​

typescript
const user = new User({ id: '1', name: 'John', createdAt: new Date() });

// Explicit JSON string — use this when you need a string:
const jsonStr = user.$qToJSON();
// '{"id":"1","name":"John","createdAt":"2024-01-01T00:00:00.000Z"}'

// Restore from string:
const restored = User.fromJSON(jsonStr);

// Or use JSON.stringify(model) which calls toJSON() automatically:
const jsonStr2 = JSON.stringify(user); // same result

$qToPlain() ​

$qToPlain(): Record<string, unknown>

Returns a plain Record<string, unknown> with current runtime values (dates stay as Date, bigints as bigint, etc. — no serialization).

Returns ​

Record<string, unknown>

See ​

Example ​

typescript
const plain = user.$qToPlain();
plain.createdAt instanceof Date; // true

$qToReadableStream() ​

Call Signature ​

$qToReadableStream(options): IQMultipartStream

Creates a ReadableStream<Uint8Array> from a binary field, or a full multipart/form-data stream from all model fields.

Parameters ​
options ​

IToReadableStreamMultipart

Returns ​

IQMultipartStream

See ​

QModel.fromStream — inverse: populate a field from a readable stream

Call Signature ​

$qToReadableStream(options): ReadableStream<Uint8Array<ArrayBufferLike>>

Creates a ReadableStream<Uint8Array> from a binary field, or a full multipart/form-data stream from all model fields.

Parameters ​
options ​

IToReadableStreamSingleField

Returns ​

ReadableStream<Uint8Array<ArrayBufferLike>>

See ​

QModel.fromStream — inverse: populate a field from a readable stream


$qUnsubscribe() ​

$qUnsubscribe(callback): void

Removes a previously registered observer callback.

If the callback was not registered, this method is a safe no-op.

Parameters ​

callback ​

IQObserverFn

Returns ​

void

Example ​

typescript
const onChange = ({ field }: IQChange) => console.log(field);
user.$qSubscribe(onChange);
// Later:
user.$qUnsubscribe(onChange);

$qValidate() ​

Call Signature ​

$qValidate(options): Promise<IQValidationReport>

Unified validation method combining integrity checks and @QRule evaluation.

Parameters ​
options ​

IQValidateOptions & object

Returns ​

Promise<IQValidationReport>

Call Signature ​

$qValidate(options?): IQValidationReport

Unified validation method combining integrity checks and @QRule evaluation.

Parameters ​
options? ​

IQValidateOptions & object

Returns ​

IQValidationReport


$qValidationReport() ​

$qValidationReport(): IQValidationReport

Returns the combined integrity + rules validation report.

Returns ​

IQValidationReport


$qValidationReportAsync() ​

$qValidationReportAsync(options?): Promise<IQValidationReport>

Async version of $qValidationReport().

Parameters ​

options? ​

IQRulesAsyncOptions

Returns ​

Promise<IQValidationReport>


collection() ​

static collection<TInst>(this, data): QModelCollection<TInst>

Creates a QModelCollection<T> from a raw data array.

This is a static alias for QModelCollection.from(Model, data) that can be called directly on the subclass.

Type Parameters ​

TInst ​

TInst extends IQCollectionItem

Parameters ​

this ​

(data) => TInst

data ​

Record<string, unknown>[]

Array of raw plain-object rows for this model.

Returns ​

QModelCollection<TInst>

A typed QModelCollection wrapping the instantiated models.

Example ​

typescript
const users = UserModel.collection(rawRows);
users.$qWhere(u => u.active).$qSortBy('name').$qPaginate(1, 10);

See ​


configure() ​

static configure(opts): IQClassConfig

Produces a per-class configuration descriptor to be assigned to the static readonly config property of a QModel subclass.

Class-level config overrides specific QConfig.defaults settings for that class only, without affecting sibling classes or the global configuration. If the same option is set both here and in the @Quick() second parameter, the @Quick() option takes precedence.

Parameters ​

opts ​

IQClassConfig

A subset of IQAdvancedOptions to apply to this class.

Returns ​

IQClassConfig

The options object (passed through; used as-is by the population service).

Example ​

typescript
@Quick()
class InternalDto extends QModel<IInternalDto> {
  static override readonly config = QModel.configure({
    unknownPropertyPolicy: 'keep',
    coercionStrategy: 'strict',
  });
  declare id: string;
}
// InternalDto keeps unknown properties; other models are unaffected.

See ​


create() ​

static create<TClass, TInterface, TAliasMap, TResult>(this, data): TResult

Factory method to create a model instance. Functionally identical to new ModelClass(data).

The main benefit over the constructor is stricter type inference — TypeScript will enforce that data matches TInterface exactly (no extra unknown properties at the call-site when unknownPropertyPolicy: 'error' is active).

Use declare in your class to inform TypeScript of the transformed runtime types.

Type Parameters ​

TClass ​

TClass extends QModel<any, any, never>

TInterface ​

TInterface = TClass extends QModel<I, any, never> ? I : never

TAliasMap ​

TAliasMap extends Record<string, string> = TClass extends QModel<any, A, never> ? A : Record<never, never>

TResult ​

TResult = TClass

Parameters ​

this ​

(data) => TClass

data ​

INoInfer<IQAliasInput<TInterface, TAliasMap>>

Data strictly matching the model interface

Returns ​

TResult

Model instance with type-safe property access

See ​

Example ​

typescript
interface IPost {
  id: number;
  title: string;
  createdAt: string; // Backend sends ISO string
}

@Quick({ createdAt: Date })
class Post extends QModel<IPost> {
  declare id: number;
  declare title: string;
  declare createdAt: Date; // Runtime type after transformation
}

// Both forms are completely equivalent:
const post1 = Post.create({ id: 1, title: 'Hello', createdAt: '2026-01-10T00:00:00.000Z' });
const post2 = new Post({ id: 2, title: 'World', createdAt: '2026-01-10T00:00:00.000Z' });

post1.createdAt instanceof Date; // true
post2.createdAt instanceof Date; // true

createMany() ​

Call Signature ​

static createMany<TClass, TInterface, TAliasMap, TResult>(this, data, options?): IQCreateManyResult<TResult>

Creates multiple model instances from an array of plain objects.

All items are processed regardless of validation failures — no fail-fast. Items that fail $qIsValid() (integrity checks or @QRule violations) are collected in errors[] and excluded from instances[] by default.

Type Parameters ​
TClass ​

TClass extends QModel<any, any, never>

TInterface ​

TInterface = TClass extends QModel<I, any, never> ? I : never

TAliasMap ​

TAliasMap extends Record<string, string> = TClass extends QModel<any, A, never> ? A : Record<never, never>

TResult ​

TResult = TClass

Parameters ​
this ​

(data) => TClass

data ​

INoInfer<IQAliasInput<TInterface, TAliasMap>>[]

Array of plain objects to deserialize

options? ​

IQCreateManyOptions

Optional configuration

Returns ​

IQCreateManyResult<TResult>

{ instances, errors } — see IQCreateManyResult

See ​
Example ​
typescript
const { instances, errors } = UserModel.createMany(rawList);
// errors[n].index   — position in original array
// errors[n].instance — the (invalid) model instance
// errors[n].errors  — combined integrity + rule failures

// Include invalid instances in the result too:
const { instances } = UserModel.createMany(rawList, { includeErrorInstances: true });

Call Signature ​

static createMany(data, options?): IQCreateManyResult<any>

Creates multiple model instances from an array of plain objects.

All items are processed regardless of validation failures — no fail-fast. Items that fail $qIsValid() (integrity checks or @QRule violations) are collected in errors[] and excluded from instances[] by default.

Parameters ​
data ​

Record<string, unknown>[]

Array of plain objects to deserialize

options? ​

IQCreateManyOptions

Optional configuration

Returns ​

IQCreateManyResult<any>

{ instances, errors } — see IQCreateManyResult

See ​
Example ​
typescript
const { instances, errors } = UserModel.createMany(rawList);
// errors[n].index   — position in original array
// errors[n].instance — the (invalid) model instance
// errors[n].errors  — combined integrity + rule failures

// Include invalid instances in the result too:
const { instances } = UserModel.createMany(rawList, { includeErrorInstances: true });

createReadonly() ​

static createReadonly<T, TClass, TResult>(this, data): Readonly<TResult>

Creates a READ-ONLY (immutable) instance of the model. The instance and all nested properties will be recursively frozen.

Equivalent to Object.freeze(new Model(data)) but applied deeply — all nested objects are frozen recursively. Use this when you want compile-time and runtime guarantees that the instance cannot be mutated.

Type Parameters ​

T ​

T extends IQAnyRecord = IQAnyRecord

TClass ​

TClass extends object = object

TResult ​

TResult = TClass

Parameters ​

this ​

(...args) => TClass

data ​

T

Data to initialize the model

Returns ​

Readonly<TResult>

Deeply frozen model instance

See ​

QModel.create — mutable variant (new Model(data) equivalent)

Example ​

typescript
// createReadonly is equivalent to a deep-frozen `new User(data)`:
const user = User.createReadonly({ name: 'John' });
user.name = 'Jane'; // ❌ Throws TypeError in strict mode

// For a mutable instance use `new` or `create`:
const mutableUser = new User({ name: 'John' });
mutableUser.name = 'Jane'; // ✅ OK

deserialize() ​

static deserialize<T>(this, data): T

Creates a model instance from a plain interface object.

Deserializes a plain JavaScript object into a fully typed model instance, applying all type transformations (string → Date, string → BigInt, etc.) according to the @QType decorators defined in the model.

SOLID - Open/Closed: Allows creating instances from interfaces without modification.

Type Parameters ​

T ​

T extends QModel<IQAnyRecord, Record<never, never>, never>

The model class type

Parameters ​

this ​

(data) => T

data ​

IQModelData<IQAnyRecord>

Plain object matching the model's interface structure

Returns ​

T

A new, fully typed model instance

See ​

Examples ​

Basic deserialization

typescript
const userData = {
  id: '1',
  name: 'John',
  createdAt: '2024-01-01T00:00:00.000Z'
};

const user = User.deserialize(userData);
user instanceof User;            // → true
user.createdAt instanceof Date;  // → true

Deserialization with type transformations

typescript
const accountData = {
  id: '123',
  balance: '999999',  // Will transform to bigint
  pattern: { source: 'test', flags: 'gi' } // Will transform to RegExp
};

const account = Account.deserialize(accountData);
typeof account.balance;              // → 'bigint'
account.pattern instanceof RegExp;  // → true

deserializeJson() ​

static deserializeJson<T>(this, json): T

Alias for fromJSON. Creates a model instance from a JSON string.

Parses a JSON string and deserializes it into a fully typed model instance. Use this when you prefer a deserialize-style naming convention over fromJSON.

Type Parameters ​

T ​

T extends QModel<IQAnyRecord, Record<never, never>, never>

The model class type

Parameters ​

this ​

(data) => T

json ​

string

JSON string representation of the model

Returns ​

T

A new, fully typed model instance

Throws ​

If json is not valid JSON

See ​

Example ​

typescript
const json = user.$qToJSON();
const restored = User.deserializeJson(json);
restored.createdAt instanceof Date; // true

extends() ​

static extends<TRuntime>(ExternalBase): typeof QModel & (...args) => TRuntime & QModel<IQAnyRecord, Record<never, never>, never>

Mixin factory that injects full QModel functionality into a class that must extend an external (third-party / framework) base class.

Use this when you cannot extend QModel directly because you already need to extend another class (e.g. an Angular component, a NestJS entity, a custom base).

The returned class:

  • Extends ExternalBase (prototype chain intact: instance instanceof ExternalBase === true)
  • Exposes all static QModel methods: create(), createReadonly(), mock(), getMetadata(), deserialize(), deserializeJson()
  • Exposes all instance QModel methods: $qSerialize(), $qToJSON(), $qToInterface(), $qIsDirty(), $qGetChangedFields(), $qReset(), $qPatch(), $qCopy()
  • Works with @Quick and @QType decorators on the derived class
  • Is NOT an instanceof QModel (different prototype chain — this is expected)

Pass a single TRuntime generic describing the expected instance type:

  • For an external base with no field-type changes: pass the class directly.
  • To change the type of inherited fields: compose with IQImplements.

Type Parameters ​

TRuntime ​

TRuntime extends object = object

The instance type of the resulting mixin. Use the external base class directly when there are no field overrides, or IQImplements<Base, { field: NewType }> to change the type of specific inherited fields.

Parameters ​

ExternalBase ​

(...args) => any

The external class to extend

Returns ​

typeof QModel & (...args) => TRuntime & QModel<IQAnyRecord, Record<never, never>, never>

A mixin base class ready to be extended

Examples ​

Basic usage — external base, no field overrides

typescript
class NgComponent { ngOnInit(): void {} }

@Quick({ createdAt: Date })
class UserModel extends QModel.extends<NgComponent>(NgComponent) {
  declare name: string;
  declare createdAt: Date;
}
instance.ngOnInit(); // ✅ TypeScript knows about NgComponent methods

Overriding inherited field types with IQImplements

typescript
@Quick({ value: Date })
class BaseModel extends QModel<any> { declare value: Date; }

@Quick({ value: BigInt })
class Derived extends QModel.extends<IQImplements<BaseModel, { value: bigint }>>(BaseModel) {
  declare value: bigint; // ✅ no conflict — value: Date removed from intersection
}

fromJSON() ​

static fromJSON<T>(this, json): T

Creates a model instance from a JSON string.

Parses a JSON string and deserializes it into a fully typed model instance, applying all type transformations (string → Date, string → BigInt, etc.).

Naming convention: fromJSON follows the standard JS/ecosystem convention (e.g. Date.prototype.toJSON → restore with Model.fromJSON). It does NOT follow any special QuickModel protocol — for that, see $qToJSON() / serialize().

Typical round-trip:

// Serialize → string
const str = user.$qToJSON();          // explicit string
const str = JSON.stringify(user);     // via JS protocol (calls toJSON())

// Restore → model
const user2 = User.fromJSON(str);

Type Parameters ​

T ​

T extends QModel<IQAnyRecord, Record<never, never>, never>

The model class type

Parameters ​

this ​

(data) => T

json ​

string

JSON string representation of the model

Returns ​

T

A new, fully typed model instance

Throws ​

If json is not valid JSON

See ​

Example ​

typescript
const json = user.$qToJSON();
// or: const json = JSON.stringify(user);

const restored = User.fromJSON(json);
restored instanceof User;              // → true
restored.createdAt instanceof Date;   // → true

fromSchema() ​

static fromSchema<T>(format, schema, className?): string

Converts a formal schema back into a QuickModel class definition (TypeScript source).

This is the inverse of QModel.getSchema():

typescript
// Forward: model → schema
const json = User.getSchema('json');

// Reverse: schema → model class source
const code = QModel.fromSchema('json', json, 'User');

Supported formats:

  • 'json' — JSON Schema Draft-07 object
  • 'openapi' — OpenAPI 3.0 component schema or full document
  • 'ajv' — AJV-compatible JSON Schema (same structure as 'json')
  • 'typescript' — TypeScript interface source string

Type Parameters ​

T ​

T extends IFromSchemaFormat

Parameters ​

format ​

T

Schema format to parse.

schema ​

IFromSchemaInput<T>

The schema to convert (type depends on format).

className? ​

string

Optional class name override. Falls back to schema.title or 'GeneratedModel'.

Returns ​

string

TypeScript source code string for a class extending QModel.

Example ​

typescript
import 'quickmodel/schema'; // register schema generators first

const jsonSchema = User.getSchema('json');
const code = QModel.fromSchema('json', jsonSchema, 'User');
// → complete TypeScript source for class User extends QModel<IUser>

See ​

  • SchemaToModelService — service powering this method
  • QFromSchemaTool — MCP tool wrapping this method

getFormSchema() ​

static getFormSchema(): IQFormSchemaEntry[]

Static version of getFormSchema() — no instance required.

Returns ​

IQFormSchemaEntry[]

Ordered array of IQFormSchemaEntry — one per @QField-decorated property.

See ​

QModel.getFormSchemaGrouped — same schema organised by @QGroup sections

Example ​

typescript
const schema = ProfileModel.getFormSchema();

getFormSchemaGrouped() ​

static getFormSchemaGrouped(): IQFormSchemaGroup[]

Static version of getFormSchemaGrouped() — no instance required.

Returns ​

IQFormSchemaGroup[]

Ordered array of { group, fields } entries — see IQFormSchemaGroup.

See ​

QModel.getFormSchema — flat (un-grouped) list


getMetadata() ​

static getMetadata(): Map<string, { transformer: unknown; type: string; }>

Gets metadata information for all registered qtypes in this model class. Returns a Map with field names as keys and metadata objects containing type and transformer info.

Returns ​

Map<string, { transformer: unknown; type: string; }>

Map of field metadata with type names and transformer information

Example ​

typescript
const metadata = User.getMetadata();
metadata.forEach((meta, fieldName) => {
  `${fieldName}: type=${meta.type}, transformer=${meta.transformer?.name}`;
  // → 'id: type=String, transformer=undefined'
  // → 'createdAt: type=Date, transformer=DateTransformer'
});

getSchema() ​

static getSchema<T>(type): IQSchemaReturnType<T>

Generate schema in multiple formats for validation, documentation, and integration.

Unified API - One method for all schema types:

  • 'json' - JSON Schema Draft-07 (universal standard)
  • 'zod' - Zod validation schema (popular TypeScript validator)
  • 'mongo' - MongoDB/Mongoose schema definition
  • 'typescript' - TypeScript interface string
  • 'graphql' - GraphQL SDL type definition
  • 'openapi' - OpenAPI 3.0 schema
  • 'ajv' - AJV validator schema

SOLID - Open/Closed: Extensible to new schema types without modifying core.

Type Parameters ​

T ​

T extends IQSchemaType

Parameters ​

type ​

T

Schema type to generate

Returns ​

IQSchemaReturnType<T>

Generated schema in the requested format

Throws ​

Error if schema type is unknown

See ​

QModel.$qGetSchema — instance variant that enriches the schema with actual example values

Examples ​

Generate JSON Schema

typescript
@Quick({ createdAt: Date, balance: BigInt })
class User extends QModel<IUser> {
  declare id: number;
  declare name: string;
  declare createdAt: Date;
  declare balance: bigint;
}

const jsonSchema = User.getSchema('json');
// {
//   $schema: 'http://json-schema.org/draft-07/schema#',
//   type: 'object',
//   title: 'User',
//   properties: {
//     id: { type: 'number' },
//     name: { type: 'string' },
//     createdAt: { type: 'string', format: 'date-time' },
//     balance: { type: 'string', pattern: '^-?\\d+$' }
//   },
//   required: ['id', 'name', 'createdAt', 'balance']
// }

Generate Zod Schema

typescript
const zodSchema = User.getSchema('zod');

// Validate data
const result = zodSchema.safeParse({
  id: 1,
  name: 'John',
  createdAt: '2024-01-01T00:00:00.000Z',
  balance: '999999999999'
});

result.success; // → true

Generate MongoDB Schema

typescript
const mongoSchema = User.getSchema('mongo');
// {
//   id: { type: Number, required: true },
//   name: { type: String, required: true },
//   createdAt: { type: Date, required: true },
//   balance: { type: String, required: true }
// }

Generate TypeScript Interface

typescript
const tsInterface = User.getSchema('typescript');
// "interface IUser {\n\tid: number;\n\tname: string;\n\tcreatedAt: Date;\n\tbalance: bigint;\n}"

Generate GraphQL Type

typescript
const graphqlType = User.getSchema('graphql');
// "type User {\n\tid: Float!\n\tname: String!\n\tcreatedAt: DateTime!\n\tbalance: String!\n}"

initialize() ​

protected initialize(inputData?): void

Initializes the model instance by deserializing the input data.

SOLID - Single Responsibility: Only initializes, delegates deserialization to service.

Parameters ​

inputData? ​

IQModelData<TInterface> | QModel<TInterface, Record<never, never>, never>

Returns ​

void


mock() ​

static mock<T>(this): QMockBuilder<IQModelInstance<T>, IQModelInterface<T>>

Creates a type-safe mock builder for generating test data. Each derived class automatically infers its correct types.

Type Parameters ​

T ​

T extends (...args) => QModel<IQAnyRecord>

The model class constructor type

Parameters ​

this ​

T

Returns ​

QMockBuilder<IQModelInstance<T>, IQModelInterface<T>>

A QMockBuilder instance specialized for this model class

Example ​

typescript
const user = User.mock().random(); // returns User
const users = User.mock().array(5); // returns User[]

toJSON() ​

Call Signature ​

toJSON(options): IQAliasedSerializedInterface<TInterface, TAliasMap>

Implements the JS toJSON protocol — returns the plain serialized object.

When you call JSON.stringify(model), JavaScript internally calls model.toJSON() and serializes the returned value. This method returns the plain object produced by $qSerialize(), so JSON.stringify(model) produces the correct JSON string without any double-encoding.

Key distinction from $qToJSON():

MethodReturnsUse case
toJSON()Plain objectJS protocol — JSON.stringify(model)
$qToJSON(options?)JSON stringExplicit string with QuickModel options

For QuickModel serialization options (@QSensitive, omit, etc.), use $qToJSON(options) or serialize(undefined, options) directly.

Parameters ​
options ​

IQSerializationOptions & object

Returns ​

IQAliasedSerializedInterface<TInterface, TAliasMap>

Plain serialized object (same as $qSerialize())

See ​
Example ​
typescript
const user = new User({ id: '1', name: 'John', createdAt: new Date() });

// JS protocol: JSON.stringify calls toJSON() automatically
const jsonStr = JSON.stringify(user);
// '{"id":"1","name":"John","createdAt":"2024-01-01T00:00:00.000Z"}'

// Direct access: toJSON() returns a plain object (sensitive fields excluded)
const plain = user.toJSON();
// { id: '1', name: 'John', createdAt: '2024-01-01T00:00:00.000Z' }

// Include sensitive fields explicitly:
const full = user.toJSON({ includeSensitive: true });

Call Signature ​

toJSON(options?): IQSafeSerializedInterface<TInterface, TAliasMap, TSensitiveKeys>

Implements the JS toJSON protocol — returns the plain serialized object.

When you call JSON.stringify(model), JavaScript internally calls model.toJSON() and serializes the returned value. This method returns the plain object produced by $qSerialize(), so JSON.stringify(model) produces the correct JSON string without any double-encoding.

Key distinction from $qToJSON():

MethodReturnsUse case
toJSON()Plain objectJS protocol — JSON.stringify(model)
$qToJSON(options?)JSON stringExplicit string with QuickModel options

For QuickModel serialization options (@QSensitive, omit, etc.), use $qToJSON(options) or serialize(undefined, options) directly.

Parameters ​
options? ​

IQSerializationOptions

Returns ​

IQSafeSerializedInterface<TInterface, TAliasMap, TSensitiveKeys>

Plain serialized object (same as $qSerialize())

See ​
Example ​
typescript
const user = new User({ id: '1', name: 'John', createdAt: new Date() });

// JS protocol: JSON.stringify calls toJSON() automatically
const jsonStr = JSON.stringify(user);
// '{"id":"1","name":"John","createdAt":"2024-01-01T00:00:00.000Z"}'

// Direct access: toJSON() returns a plain object (sensitive fields excluded)
const plain = user.toJSON();
// { id: '1', name: 'John', createdAt: '2024-01-01T00:00:00.000Z' }

// Include sensitive fields explicitly:
const full = user.toJSON({ includeSensitive: true });

Properties ​

config? ​

readonly static optional config?: Partial<IQAdvancedOptions<Record<string, unknown>>>

Optional per-class configuration override.

Assign the result of QModel.configure({...}) here to override specific QConfig.defaults settings for this class only, without affecting siblings or the global configuration.

Example ​

typescript
@Quick()
class InternalDto extends QModel<IInternalDto> {
  static override readonly config = QModel.configure({
    unknownPropertyPolicy: 'keep',
  });
}

See ​

QModel.configure — factory method that produces this value

Serialization ​

fromFormData() ​

static fromFormData<TClass>(this, formData, options?): TClass

Creates a model instance from a FormData object.

Converts the FormData entries into a plain object and passes it to the model constructor. File and Blob entries are resolved according to the fileSource option (default: 'auto').

Auto-detection tree (default fileSource: 'auto'):

  • File instance → kept as File
  • Blob instance → kept as Blob
  • string "data:..." → decoded base64 → Blob
  • string "https://..." → string (URL reference)
  • Any other string → string as-is

Type Parameters ​

TClass ​

TClass extends QModel<IQAnyRecord, Record<never, never>, never>

Parameters ​

this ​

(data) => TClass

formData ​

FormData

Source FormData

options? ​

IFromFormDataOptions

Conversion options (fileSource, per-field overrides)

Returns ​

TClass

Model instance with type-safe property access

See ​

QModel.$qToFormData — inverse: serialize a model instance back to FormData

Example ​

typescript
// Default auto-detection
const dto = UploadDto.fromFormData(formData);

// Force all binary fields to be treated as references
const dto = UploadDto.fromFormData(formData, { fileSource: 'reference' });

// Per-field overrides
const dto = UploadDto.fromFormData(formData, {
  fields: { avatar: 'binary', document: 'reference' },
});

fromStream() ​

static fromStream<TClass>(this, stream, options): Promise<TClass>

Creates a model instance with a binary field populated from a ReadableStream<Uint8Array>.

All stream chunks are accumulated into a single Blob and assigned to the specified field. Use when you need the full binary data in memory (e.g. after receiving a small upload).

Type Parameters ​

TClass ​

TClass extends QModel<IQAnyRecord, Record<never, never>, never>

Parameters ​

this ​

(data) => TClass

stream ​

ReadableStream<Uint8Array<ArrayBufferLike>>

Source ReadableStream<Uint8Array>

options ​

IFromStreamOptions

Must include field (name of the target binary field)

Returns ​

Promise<TClass>

Promise<TClass> — model instance with the binary field set

Throws ​

RangeError if maxBytes is set and the stream exceeds it

See ​

Example ​

typescript
const dto = await UploadDto.fromStream(req.body, { field: 'video', maxBytes: 500 * 1024 * 1024 });
// dto.video → Blob with all stream chunks

fromURL() ​

static fromURL<TClass>(this, params): TClass

Creates a model instance from a URLSearchParams object.

Reads each key from params using the correct strategy:

  • Fields declared as array specs in @Quick({ field: [Type] }) → params.getAll(key)
  • All other fields → params.get(key)

After extraction, the plain object is passed to the normal constructor so all registered type coercions (Number, Date, [Number], custom transformers…) are applied automatically.

Type Parameters ​

TClass ​

TClass extends QModel<IQAnyRecord, Record<never, never>, never>

Parameters ​

this ​

(data) => TClass

params ​

URLSearchParams

Source URLSearchParams (e.g. new URL(req.url).searchParams, request.nextUrl.searchParams, or new URLSearchParams(window.location.search))

Returns ​

TClass

Model instance with type-safe, coerced property access

See ​

Example ​

typescript
// URL: /search?role=admin&age=25&tags=read&tags=write

@Quick({ age: Number, tags: [String] })
class SearchDto extends QModel<ISearch> {
  declare role: string;
  declare age: number;
  declare tags: string[];
}

// Next.js App Router
const dto = SearchDto.fromURL(request.nextUrl.searchParams);
dto.age;  // 25  (number, not '25')
dto.tags; // ['read', 'write']

// Hono / Express
const dto = SearchDto.fromURL(new URL(req.url).searchParams);

pipeStream() ​

static pipeStream(src, dst, options?): Promise<void>

Pipes all bytes from a ReadableStream<Uint8Array> to a WritableStream<Uint8Array> without accumulating anything in memory.

QModel acts as a zero-copy conduit. Use for large files or server-side proxy uploads.

Parameters ​

src ​

ReadableStream<Uint8Array<ArrayBufferLike>>

Source readable stream

dst ​

WritableStream<Uint8Array<ArrayBufferLike>>

Destination writable stream

options? ​

IPipeStreamOptions

Optional limits and progress callback

Returns ​

Promise<void>

Promise<void> — resolves when all bytes have been piped

Throws ​

RangeError if maxBytes is set and the stream exceeds it

See ​

Example ​

typescript
await UploadDto.pipeStream(req.body, s3UploadStream, { maxBytes: 500 * 1024 * 1024 });