Installation
Prerequisites
Before installing QuickModel, ensure you have:
- Node.js >= 18.0.0
- TypeScript >= 3.4.0
- A package manager: npm, yarn, pnpm, or bun
TypeScript version compatibility
QuickModel avoids TypeScript built-ins that would raise the minimum version. The table below lists every version-sensitive feature and how it is handled:
| Feature | Where used | Introduced in | How we handle it |
|---|---|---|---|
Mapped types, indexed access (T[number]) | IQGroupsMap<T>, qGroups() return type | TS 2.1 | Native |
Conditional types (INoInfer<T>) | polyfill for QModel.create() | TS 2.8 | Native (our own polyfill, no built-in used) |
readonly T[] shorthand in generics | qGroups() overload | TS 3.4 | Native (sets the floor) |
ClassFieldDecoratorContext | @QType / @QRule TC39 overload | TS 5.0 | Polyfilled as IClassFieldDecoratorCtx (internal) |
NoInfer<T> built-in | QModel.create() / QModel.createMany() | TS 5.4 | Polyfilled as INoInfer<T> (internal) |
const type parameters | qGroups5() / compat/ts5/forms entry point | TS 5.0 | Isolated in separate /compat/ts5/forms entry point |
The main package (quickmodel) works with TypeScript 3.4+. The /compat/ts5/forms entry point requires TypeScript 5.0+ due to const type parameters.
Install QuickModel
Choose your preferred package manager:
npm install quickmodelTypeScript Configuration
QuickModel supports two decorator modes. Pick whichever matches your project:
Mode 1 — Legacy decorators (classic, widest tooling compatibility)
{
"compilerOptions": {
"experimentalDecorators": true,
"target": "ES2022",
"lib": ["ES2022"],
"module": "ESNext",
"moduleResolution": "node"
}
}Mode 2 — TC39 standard decorators (TypeScript 5+, no legacy flags)
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "ESNext",
"moduleResolution": "node"
}
}TC39 mode — what changes?
When experimentalDecorators is absent or false, TypeScript compiles decorators using the TC39 Stage-3 spec. QuickModel handles this transparently:
@Quick— unchanged. Class decorators still receive the class constructor as their first argument.@QType— metadata is now registered inside anaddInitializercallback that fires on first instance creation instead of at class-definition time. For practical purposes this is invisible — the metadata is always ready beforeQModel.initialize()reads it.@QRule— the predicate parameter type is automatically inferred from the field type. No manual annotation needed:
// TC39 mode — val inferred as Date automatically ✅
@QRule((val) => val > new Date('2000-01-01'), 'Must be after 2000')
createdAt!: Date;
// Legacy mode — annotation required
@QRule((val: Date) => val > new Date('2000-01-01'), 'Must be after 2000')
declare createdAt: Date;Field syntax changes with TC39
In TC39 mode, field decorators (@QType, @QRule) cannot be applied to declare fields — use ! (definite assignment assertion) instead:
// ✅ TC39 mode
@QType(Date)
createdAt!: Date;
// ✅ Legacy mode (experimentalDecorators: true)
@QType(Date)
declare createdAt: Date;Undecorated fields that only need type-tracking (no @QType / @QRule) can still use declare in both modes.
Required Compiler Options
experimentalDecorators: true(legacy mode only) — enables legacy PropertyDecorator syntax. Omit it (or set tofalse) to use TC39 mode.
Recommended Options
strict: true- Enable all TypeScript strict type-checking options (unrelated to QuickModel'sunknownPropertyPolicy)target: "ES2020"- Modern JavaScript featuresmodule: "ESNext"- Modern module system
emitDecoratorMetadata is NOT required
Unlike many other libraries, QuickModel does NOT require "emitDecoratorMetadata": true.
QuickModel relies on explicit type mapping (e.g., @Quick({ date: Date })) as the source of truth. This ensures robust behavior regardless of your compiler settings or build tool (esbuild, swc, babel, etc.).
Specific Case: When using @QType() without arguments, QuickModel attempts to read metadata. If emitDecoratorMetadata is disabled, it simply falls back to treating the value "as-is" (no transformation). This is perfectly fine for primitives but means you must use explicit mapping for special types (Date, BigInt, etc.).
Import Structure
QuickModel uses a modular import structure to keep your project clean:
- Core: Main classes and decorators (
QModel,Quick,QType)typescriptimport { QModel, Quick } from 'quickmodel'; - Type Definitions: Interfaces and helper types (
IQSerializedInterface,IQSpec, etc.)typescriptimport type { IQSerializedInterface } from 'quickmodel/types'; - Advanced Utilities: Runtime utilities for power users (
QMockGenerator)typescriptimport { QMockGenerator } from 'quickmodel/advanced';
CLI Scaffolding
After installing, you can scaffold boilerplate with the built-in CLI:
# Generate a QModel class with typed fields
npx quickmodel generate model User --fields "id:number,name:string,createdAt:Date"
# Generate a custom transformer skeleton
npx quickmodel generate transformer Decimal
# Generate integration boilerplate (prisma | drizzle | zod)
npx quickmodel generate integration prismaTIP
With Bun you can use bunx quickmodel generate ... instead of npx.
Verify Installation
Create a simple test file to verify everything works:
import { QModel, Quick } from 'quickmodel';
interface IUser {
id: number;
name: string;
createdAt: string;
}
@Quick({ createdAt: Date })
class User extends QModel<IUser> {
declare id: number;
declare name: string;
declare createdAt: Date;
}
const user = new User({
id: 1,
name: 'John Doe',
createdAt: '2026-01-10T00:00:00.000Z',
});
console.log(user.createdAt instanceof Date); // true ✅If this runs without errors and prints true, you're all set!
Next Steps
- Quick Start - Build your first model
- QModel - Learn about the base model class
- Examples - See real-world examples