Frontend
TypeScript
TypeScript conventions for frontend types and interfaces.
Type Files
Types live in app/types/ per app or in codi-layer/app/types/ for shared types. They are auto-imported by Nuxt.
app/types/
index.ts ← barrel export
auth.ts ← TUser, TUserType, etc.
booking.ts ← IBooking, TBookingStatus, etc.
form.ts ← TValidationRule, TValidationRules, TApiActionOptions
payment.ts ← TPaymentType, TCreatePayment
Naming Conventions
| Pattern | When to use | Example |
|---|---|---|
I prefix (interface) | Entity/model shapes with optional fields | IBooking, IUser |
T prefix (type alias) | Derived types, unions, utility types | TUser, TBookingStatus |
| No prefix | Simple prop types, local-only types | Props, Emits |
Common Types
// types/auth.ts
export interface IUser {
_id: string
firstName: string
lastName: string
email: string
phone: string
type: TUserType
organizationId?: string
status?: TUserStatus
createdAt?: string
updatedAt?: string
}
export type TUser = IUser
export type TUserType = 'ADMIN' | 'OWNER' | 'BRANCH_MANAGER' | 'CUSTOMER'
export type TUserStatus = 'ACTIVE' | 'INACTIVE' | 'SUSPENDED'
// types/form.ts
export type TValidationRule = (value: any) => string
export type TValidationRules = Record<string, TValidationRule>
export interface TApiActionOptions {
successMessage?: string
errorMessage?: string
skipAlert?: boolean
onSuccess?: (result: any) => void | Promise<void>
onError?: (error: any) => void
}
defineProps and defineEmits Typing
Always use the generic form (not the object runtime form):
// ✅ Correct
interface Props {
booking: IBooking
showActions?: boolean
}
const props = withDefaults(defineProps<Props>(), { showActions: true })
// ❌ Avoid
const props = defineProps({
booking: Object as PropType<IBooking>,
showActions: { type: Boolean, default: true },
})
// ✅ Correct
const emit = defineEmits<{
cancel: [id: string]
reschedule: [id: string]
close: []
}>()
// ❌ Avoid
const emit = defineEmits(['cancel', 'reschedule', 'close'])
API Response Typing
Type all $api calls at the call site:
const response = await useNuxtApp().$api<{
bookings: IBooking[]
pages: number
total: number
}>('/api/bookings', { method: 'GET', query: { page: 1 } })
For repeated shapes, extract a shared type:
// types/api.ts
export interface TPaginatedResponse<T> {
items: T[]
pages: number
total: number
page: number
limit: number
}
Strict Null Checks
tsconfig.json has strict: true. Always handle possible null/undefined:
// ✅
const name = currentUser.value?.firstName ?? 'Guest'
// ❌
const name = currentUser.value.firstName