TypeScript Support
The @nhtio/web-re-active-record
library is built with TypeScript from the ground up, providing comprehensive type safety and excellent developer experience. This guide will help you understand the type system and how to effectively use types in your application.
Type Safety
The library provides strong type safety through:
- Compile-time Type Checking: All operations on models and relationships are fully typed
- Generic Type Parameters: Models and queries maintain type information throughout the chain
- Type Inference: TypeScript can automatically infer types in most cases
- Strict Null Checks: The library is designed to work well with TypeScript's strict mode
- Runtime Type Guards: Built-in error classes support
instanceof
checks for robust error handling
Essential Types
Core Model Types
ReactiveModel<T, PK, R>
: The base interface for all reactive modelsT
: The shape of your model dataPK
: The primary key typeR
: The shape of your model's relationships
ReactiveModelConstructor
: The constructor interface for reactive modelsBaseReactiveModel
: The abstract base class that implements core model functionalityPlainObject
: Represents a basic object with string keys and unknown values
Data Types
DataValues<T, PK>
: Represents model properties excluding the primary keyDataProps<T>
: Extracts only the data properties (non-methods) from a typePendingStateChange
: Represents pending changes to a model's state
Query and Database Types
ReactiveQueryBuilder<T, PK, R>
: Type for building and executing queriesReactiveDatabaseOptions
: Configuration options for the databaseReactiveDatabaseModelDefinition
: Type for defining model schemasReactiveDatabaseInitialOptions
: Initial database configurationReActiveDatabaseDexie<T>
: Extended Dexie database type with entity tablesReactiveStateTypedEventMap
: Map of all possible state change events in the system
Event and Change Tracking Types
ReactiveModelChangeEmitterEvent
: Events emitted by modelsReactiveModelChangeEmitterEventMap
: Map of all possible model eventsReactiveModelChangeDelta
: Represents changes in model dataLogBusEvent
: Type for logging events
Where to Find Types
The library exports its types from several locations:
Types Module:
typescriptimport type { ReactiveModel, PlainObject, DataValues } from '@nhtio/web-re-active-record/types'
Errors Module:
typescriptimport { ReactiveModelQueryException, NoReactiveModelRecordError } from '@nhtio/web-re-active-record/errors'
Working with Query Builder Types
import type { ReactiveQueryBuilder } from '@nhtio/web-re-active-record/types'
// The query builder maintains type information
const query: ReactiveQueryBuilder<User, 'id', {}> = UserModel.query()
const user = await query
.where('email', 'user@example.com')
.first()
// user is typed as User | undefined
Error Handling
The library exports error classes from the errors module that support instanceof
checks. Here are some common error scenarios:
Database Operations
import {
NoReactiveModelRecordError,
MissingReactiveModelRecordError,
ReactiveModelQueryException
} from '@nhtio/web-re-active-record/errors'
try {
const user = await UserModel.findOrFail(id)
} catch (error) {
if (error instanceof NoReactiveModelRecordError) {
// Handle no records found
} else if (error instanceof ReactiveModelQueryException) {
// Handle query execution errors
}
}
Model Operations
import {
ReactiveModelDeletedException,
ReactiveModelNoSuchPropertyException,
ReactiveModelUnacceptableValueException
} from '@nhtio/web-re-active-record/errors'
try {
model.someProperty = value
} catch (error) {
if (error instanceof ReactiveModelNoSuchPropertyException) {
// Handle invalid property access
} else if (error instanceof ReactiveModelUnacceptableValueException) {
// Handle invalid value assignment
} else if (error instanceof ReactiveModelDeletedException) {
// Handle operations on deleted model
}
}
Relationship Handling
import {
UnpreparedRelationshipException,
RelationshipNotBootedException
} from '@nhtio/web-re-active-record/errors'
try {
await model.related('author')
} catch (error) {
if (error instanceof UnpreparedRelationshipException) {
// Handle unprepared relationship access
} else if (error instanceof RelationshipNotBootedException) {
// Handle unbooted relationship
}
}
Type Utilities
The library provides several utility types to help with common patterns:
StringKeyOf<T>
: Extracts string keys from an object typeDataProps<T>
: Extracts only the data properties (non-methods) from a typeIsStrictlyAny<T>
: Type guard for checking if a type is strictly 'any'NonMethodKeys<T>
: Extracts keys that don't represent methodsOnlyMethodKeys<T>
: Extracts keys that represent methods
These utilities can be helpful when working with advanced type patterns in your application.
Type Safety Best Practices
Use type inference with query builders:
typescriptconst users = await UserModel.query() .where('active', true) .get() // users is typed as UserModel[]
Leverage error classes for type-safe error handling:
typescriptimport { ReactiveModelQueryException } from '@nhtio/web-re-active-record/errors' try { await model.save() } catch (error) { if (error instanceof ReactiveModelQueryException) { // Handle database operation error } }
Use relationship types for better type safety:
typescriptconst post = await PostModel.query() .with('author') // Relationship is type-checked .first()