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

PatternWhen to useExample
I prefix (interface)Entity/model shapes with optional fieldsIBooking, IUser
T prefix (type alias)Derived types, unions, utility typesTUser, TBookingStatus
No prefixSimple prop types, local-only typesProps, 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