Nuxt Guide
TypeScript
TypeScript conventions for frontend types — declaration style, naming, and patterns.
Type Files
Types live in app/types/ and are globally available via Nuxt's auto-import (declare types require no explicit import).
app/types/
index.ts ← barrel export for exported types
auth.ts ← TUser, TUserType, TUserStatus
resource.ts ← IResource, TResourceStatus
form.ts ← TValidationRule, TValidationRules, TApiActionOptions
api.ts ← TPaginatedResponse<T>
Declaration Style
Use declare type and declare interface in .d.ts files — this makes types globally available without any import statement. Use export type / export interface only in regular .ts files consumed via barrel exports.
// types/auth.d.ts — globally available, no import needed
declare type TLogin = {
email: string
password: string
}
declare type TRegister = {
id?: string
firstName: string
lastName: string
phone: string
email: string
password: string
}
declare interface IUser {
_id: string
firstName: string
lastName: string
email: string
phone?: string
role: TUserRole
status: TUserStatus
}
declare type TUserRole = 'ADMIN' | 'OWNER' | 'CUSTOMER'
declare type TUserStatus = 'ACTIVE' | 'INACTIVE' | 'PENDING'
For nested / complex request shapes, keep nesting inline:
declare type TRegisterOwner = {
personalDetails: {
firstName: string
middleName: string
lastName: string
phone: string
}
businessDetails: {
name: string
businessType: string
businessAddress: {
region: string
province: string
city: string
barangay: string
street: string
zip: string
}
}
credentials: {
email: string
password: string
}
verifiedOTP: string
}
Naming Conventions
| Pattern | When to use | Example |
|---|---|---|
I prefix (interface) | Entity/model shapes | IUser, IResource |
T prefix (type alias) | Unions, derived types, request/response shapes | TUserRole, TRegister |
| No prefix | Local component props/emits | Props, Emits |
Common Entity Type
// types/resource.d.ts
declare interface IResource {
_id: string
name: string
description?: string
status?: TResourceStatus
organizationId?: string
createdAt?: string
updatedAt?: string
}
declare type TResourceStatus = 'ACTIVE' | 'INACTIVE'
declare type TResourceCreate = Pick<IResource, 'name' | 'description' | 'organizationId'>
Form Types
// types/form.d.ts
declare type TValidationRule = (value: any) => string
declare type TValidationRules = Record<string, TValidationRule>
declare type TApiActionOptions = {
successMessage?: string
errorMessage?: string
skipAlert?: boolean
onSuccess?: (result: any) => void | Promise<void>
onError?: (error: any) => void
}
Paginated Response
// types/api.d.ts
declare interface TPaginatedResponse<T> {
items: T[]
pages: number
total: number
page: number
limit: number
}
defineProps and defineEmits Typing
Use the runtime object form for defineProps and the array form for defineEmits. Always provide a concrete PropType<T> — avoid PropType<any>.
// ✅ Correct
const props = defineProps({
modelValue: { type: Object as PropType<TAddress>, required: true },
disabled: { type: Boolean, default: false },
required: { type: Boolean, default: true },
})
const emit = defineEmits(['update:modelValue'])
// ❌ Avoid — PropType<any> defeats type safety
const props = defineProps({
modelValue: { type: Object as PropType<any>, required: true },
})
// ❌ Avoid — Options API / PropType object without a real type
const props = defineProps({
item: { type: Object, required: true },
})
PropType reference
| Prop shape | PropType to use |
|---|---|
| Object / interface | Object as PropType<IResource> |
| Array of objects | Array as PropType<IResource[]> |
| Union string | String as PropType<'ACTIVE' | 'INACTIVE'> |
| Function | Function as PropType<() => void> |
API Response Typing
Type all $api calls at the call site:
const response = await useNuxtApp().$api<TPaginatedResponse<IResource>>(
'/api/resources',
{ method: 'GET', query: { page: 1 } }
)
Strict Null Checks
tsconfig.json has strict: true. Always handle possible null/undefined:
// ✅
const name = currentUser.value?.firstName ?? 'Guest'
// ❌
const name = currentUser.value.firstName
tsconfig.json Baseline
{
"extends": "./.nuxt/tsconfig.json",
"compilerOptions": {
"strict": true
}
}