Skip to content

Model Types

In this page, we introduce the model types in the ORM library Accel Record.

NewModel and PersistedModel

Accel Record provides two types, NewModel and PersistedModel, to distinguish between newly created and saved models.
Depending on the schema definition, some properties in NewModel allow undefined, while PersistedModel does not.
This allows you to handle both pre-save and post-save models in a type-safe manner.

import { User, NewUser } from "./models/index.js";
/*
Example of NewModel:
NewUser represents a pre-save model and has the following type:
interface NewUser {
id: number | undefined;
firstName: string | undefined;
lastName: string | undefined;
age: number | undefined;
}
*/
const newUser: NewUser = User.build({});
/*
Example of PersistedModel:
User represents a saved model and has the following type:
interface User {
id: number;
firstName: string;
lastName: string;
age: number | undefined;
}
*/
const persistedUser: User = User.first()!;

BaseModel

The above NewModel and PersistedModel inherit from BaseModel.
Methods defined in BaseModel can be used by both NewModel and PersistedModel.

src/models/user.ts
import { ApplicationRecord } from "./applicationRecord.js";
/*
Example of BaseModel:
UserModel corresponds to `NewUser` and `User` in `BaseModel`.
*/
export class UserModel extends ApplicationRecord {
// Methods defined here can be used by both `NewUser` and `User`.
get fullName(): string | undefined {
if (!this.firstName || !this.lastName) {
// For `NewUser`, we need to consider the possibility of `firstName` and `lastName` being `undefined`.
return undefined;
}
return `${this.firstName} ${this.lastName}`;
}
}
import { User, NewUser } from "./models/index.js";
const newUser: NewUser = User.build({});
console.log(newUser.fullName); // => undefined
const user: User = User.first()!;
console.log(user.fullName); // => "John Doe"

You can also define methods that are type-safe and can only be used by PersistedModel by specifying the this type for the method. (In this case, the get keyword cannot be used due to TypeScript specifications)

src/models/user.ts
import { ApplicationRecord } from "./applicationRecord.js";
import { User } from "./index.js";
export class UserModel extends ApplicationRecord {
// This method can only be used by `User` and is type-safe. Using it with `NewUser` will result in a type error.
fullName(this: User): string {
return `${this.firstName} ${this.lastName}`;
}
}
import { User, NewUser } from "./models/index.js";
const newUser: NewUser = User.build({});
// @ts-expect-error
newUser.fullName();
// => The 'this' context of type 'NewUser' is not assignable to method's 'this' of type 'User'.
const user: User = User.first()!;
console.log(user.fullName()); // => "John Doe"

Converting from NewModel to PersistedModel

By using methods like save() and isPersisted(), you can convert a NewModel type to a PersistedModel type.

import { User, NewUser } from "./models/index.js";
// Prepare a user of NewModel type
const user: NewUser = User.build({
firstName: "John",
lastName: "Doe",
});
if (user.save()) {
// If save is successful, the NewModel is converted to PersistedModel.
// In this block, the user can be treated as User type.
console.log(user.id); // user.id is of type number
} else {
// If save fails, the NewModel remains the same type.
// In this block, the user remains as NewUser type.
console.log(user.id); // user.id is of type number | undefined
}
const someFunc = (user: NewUser | User) => {
if (user.isPersisted()) {
// If isPersisted() is true, the NewModel is converted to PersistedModel.
// In this block, the user can be treated as User type.
console.log(user.id); // user.id is of type number
} else {
// If isPersisted() is false, the NewModel remains the same type.
// In this block, the user remains as NewUser type.
console.log(user.id); // user.id is of type number | undefined
}
};