Sonamu automatically generates various types from Entity definitions. Not only base types, but also API parameters, Subset types, Enums, and more are auto-generated to provide complete type safety.
Generated Types Overview
Base Types Entity’s basic types: User, Post, Comment
Subset Types Partial query types: UserA, UserP, UserSS
Params Types API parameter types: ListParams, SaveParams
Enum Types Enums and helper functions: UserRole, userRoleLabel
Base Types
Basic types including all fields of the Entity.
user.entity.json
user.types.ts (auto-generated)
{
"id" : "User" ,
"props" : [
{ "name" : "id" , "type" : "integer" },
{ "name" : "email" , "type" : "string" },
{ "name" : "username" , "type" : "string" },
{ "name" : "created_at" , "type" : "date" }
]
}
Characteristics :
HasMany, ManyToMany Relations are excluded
BelongsToOne, OneToOne(hasJoinColumn) are included as {name}_id format
Virtual fields only have type definition, values are computed in Enhancer
Subset Types
Types that include only partial fields of the Entity.
user.entity.json
sonamu.generated.ts (auto-generated)
{
"subsets" : {
"A" : [ "id" , "email" , "username" , "created_at" ],
"P" : [ "id" , "email" , "employee.id" , "employee.department.name" ],
"SS" : [ "id" , "email" ]
}
}
Usage example :
import type { UserSubsetKey , UserSubsetMapping } from "./sonamu.generated" ;
// Select Subset type with generics
async function findMany < T extends UserSubsetKey >(
subset : T
) : Promise < UserSubsetMapping [ T ][]> {
// ...
}
const users = await findMany ( "A" ); // UserA[] type
const profiles = await findMany ( "P" ); // UserP[] type
ListParams Types
Parameter types for list query APIs.
user.entity.json
user.types.ts (auto-generated)
{
"props" : [
{ "name" : "email" , "type" : "string" , "toFilter" : true },
{ "name" : "username" , "type" : "string" , "toFilter" : true }
]
}
Usage example :
// API call
const result = await findManyUsers ({
num: 20 ,
page: 1 ,
search: "email" ,
keyword: "john" ,
orderBy: "created_at-desc" ,
email: [ "john@test.com" , "jane@test.com" ], // Arrays also possible
});
zArrayable : A helper that allows both single values and arrays.// Both possible
{
email : "john@test.com" ;
}
{
email : [ "john@test.com" , "jane@test.com" ];
}
SaveParams Types
Parameter types for save APIs.
user.types.ts (auto-generated)
Usage example
// SaveParams = Base type with id as optional
export const UserSaveParams = User . partial ({ id: true });
export type UserSaveParams = z . infer < typeof UserSaveParams >;
// Equivalent to:
// type UserSaveParams = {
// id?: number;
// email: string;
// username: string;
// created_at: Date;
// }
Characteristics :
Only id is optional, other fields are required
With id → UPDATE, without → INSERT
Can save multiple records simultaneously with array
Enum Types
Entity Enums are auto-generated.
user.entity.json
sonamu.generated.ts (auto-generated)
{
"enums" : {
"UserRole" : {
"admin" : "Administrator" ,
"moderator" : "Moderator" ,
"normal" : "Normal User"
}
}
}
Usage example :
import { UserRole , userRoleLabel } from "./sonamu.generated" ;
// Type-safe Enum usage
const role : UserRole = "admin" ;
// Display label
console . log ( userRoleLabel ( role )); // "Administrator"
// Generate selection options in UI
const options = UserRole . options . map (( value ) => ({
value ,
label: userRoleLabel ( value ),
}));
// [
// { value: "admin", label: "Administrator" },
// { value: "moderator", label: "Moderator" },
// { value: "normal", label: "Normal User" }
// ]
Custom Params Types
Define custom parameter types when creating additional APIs in Model.
user.types.ts
user.model.ts
// Add custom parameters
export const UserLoginParams = z . object ({
email: z . string (). email (),
password: z . string (). min ( 8 ),
rememberMe: z . boolean (). optional (),
});
export type UserLoginParams = z . infer < typeof UserLoginParams >;
export const UserRegisterParams = User . pick ({
email: true ,
username: true ,
}). extend ({
password: z . string (). min ( 8 ),
passwordConfirm: z . string (). min ( 8 ),
}). refine (
( data ) => data . password === data . passwordConfirm ,
{ message: "Passwords do not match" , path: [ "passwordConfirm" ] }
);
export type UserRegisterParams = z . infer < typeof UserRegisterParams >;
Auto-generated :
// services.generated.ts
export async function loginUser (
params : UserLoginParams
) : Promise <{ user : User ; token : string }> {
const { data } = await axios . post ( "/user/login" , { params });
return data ;
}
export function useLoginUser () {
return useMutation ({
mutationFn : ( params : UserLoginParams ) => loginUser ( params ),
});
}
Action Params Types
Parameter types for specific actions.
user.types.ts
user.model.ts
// ID array parameters
export const UserBulkDeleteParams = z . object ({
ids: z . number (). int (). array (). min ( 1 ),
});
export type UserBulkDeleteParams = z . infer < typeof UserBulkDeleteParams >;
// Status change parameters
export const UserStatusUpdateParams = z . object ({
ids: z . number (). int (). array (). min ( 1 ),
status: z . enum ([ "active" , "suspended" , "deleted" ]),
reason: z . string (). optional (),
});
export type UserStatusUpdateParams = z . infer < typeof UserStatusUpdateParams >;
ListResult Type
Type for list query results.
export type ListResult <
LP extends { num ?: number ; page ?: number ; queryMode ?: string },
T
> = LP [ "queryMode" ] extends "list"
? { rows : T [] }
: LP [ "queryMode" ] extends "count"
? { total : number }
: { rows : T []; total : number };
Usage example :
// Type is automatically determined based on queryMode
// queryMode: "both" (default)
const result = await findManyUsers ({ num: 10 , page: 1 });
// ListResult<UserListParams, User> = { rows: User[]; total: number }
// queryMode: "list"
const result = await findManyUsers ({ num: 10 , page: 1 , queryMode: "list" });
// ListResult<UserListParams, User> = { rows: User[] }
// queryMode: "count"
const result = await findManyUsers ({ queryMode: "count" });
// ListResult<UserListParams, User> = { total: number }
Conditional types : ListResult uses TypeScript conditional types to return different types based on queryMode.
Type Exports
Generated types are automatically exported.
// Re-export all types per Entity
export * from "./user/user.model" ;
export * from "./post/post.model" ;
export * from "./comment/comment.model" ;
// Model instances are also exported
export { UserModel } from "./user/user.model" ;
export { PostModel } from "./post/post.model" ;
Usage :
// Import from one place
import {
User ,
UserListParams ,
UserSaveParams ,
UserRole ,
} from "./sonamu.generated" ;
// Or individual imports
import type { User } from "./user/user.types" ;
import { UserModel } from "./user/user.model" ;
Type Utilities
Useful type utilities provided by Sonamu.
zArrayable
Type that allows both single values and arrays.
import { zArrayable } from "sonamu" ;
const UserListParams = z . object ({
id: zArrayable ( z . number (). int ()). optional (),
email: zArrayable ( z . string ()). optional (),
});
// Both possible
{
id : 1 ;
}
{
id : [ 1 , 2 , 3 ];
}
DistributiveOmit
Safer than regular Omit for Union types.
import type { DistributiveOmit } from "sonamu" ;
type UserOrAdmin =
| { type : "user" ; userId : number }
| { type : "admin" ; adminId : number };
// DistributiveOmit is applied to each type individually
type WithoutId = DistributiveOmit < UserOrAdmin , "userId" | "adminId" >;
// = { type: "user" } | { type: "admin" }
Customizing Type Generation
You can extend or modify auto-generated types.
Extending Params
// Extend basic ListParams
export const ExtendedUserListParams = UserListParams . extend ({
includeDeleted: z . boolean (). optional (),
dateRange: z
. object ({
from: z . date (),
to: z . date (),
})
. optional (),
});
export type ExtendedUserListParams = z . infer < typeof ExtendedUserListParams >;
Extending Subset Types
// Add computed fields to Subset type
export type UserAWithStats = UserA & {
post_count : number ;
follower_count : number ;
};
Creating Conditional Types
// Different types based on role
export type UserByRole < T extends UserRole > = T extends "admin"
? UserA & { permissions : string [] }
: T extends "normal"
? UserSS
: User ;
Practical Example
Example of using generated types in a real project.
user.model.ts
UserList.tsx
import type {
UserSubsetKey ,
UserSubsetMapping ,
UserListParams ,
UserSaveParams ,
UserLoginParams ,
} from "./user.types" ;
class UserModelClass extends BaseModelClass <
UserSubsetKey ,
UserSubsetMapping ,
typeof userSubsetQueries ,
typeof userLoaderQueries
> {
@ api ({ httpMethod: "GET" })
async findMany < T extends UserSubsetKey >(
subset : T ,
params ?: UserListParams
) : Promise < ListResult < UserListParams , UserSubsetMapping [ T ]>> {
// Type-safe implementation
}
@ api ({ httpMethod: "POST" })
async save ( params : UserSaveParams []) : Promise < number []> {
// Type-safe implementation
}
@ api ({ httpMethod: "POST" })
async login ( params : UserLoginParams ) : Promise <{ user : User ; token : string }> {
// Type-safe implementation
}
}
Next Steps