Our offices

  • Exceev Consulting
    61 Rue de Lyon
    75012, Paris, France
  • Exceev Technology
    332 Bd Brahim Roudani
    20330, Casablanca, Morocco

Follow us

5 min read - A Short Guide to TypeScript Component Naming: Angular and NestJS Best Practices

Frontend & Backend Development

Good naming conventions are like a well-designed API — they make your code self-documenting and reduce cognitive load for every developer who touches your codebase. In TypeScript applications, especially in enterprise frameworks like Angular and NestJS, consistent naming becomes even more critical as your application scales.

After working on dozens of Angular and NestJS projects, we have seen how poor naming decisions compound over time, creating confusion and slowing down development. Conversely, teams that establish clear naming conventions from day one move faster, onboard new developers more easily, and maintain higher code quality.

What you'll learn

  • Core TypeScript naming conventions for classes, interfaces, methods, and properties
  • Angular-specific naming patterns for components, services, directives, and pipes
  • NestJS naming patterns for controllers, services, repositories, and DTOs
  • Advanced strategies for generics, events, and callbacks
  • How to enforce conventions with ESLint and code review checklists

Quick reference

Use PascalCase for classes and interfaces, camelCase for methods and properties, and kebab-case for file names. Angular components append Component, services append Service. NestJS controllers match their resource name, DTOs indicate direction (Create, Update, Response). Enforce conventions with @typescript-eslint/naming-convention rules.

The Foundation: TypeScript Naming Conventions

TypeScript's type system gives us powerful tools for creating self-documenting code, but only if we use them consistently.

Classes and Interfaces

Classes should use PascalCase and describe what they represent:

// Good
class UserService {}
class PaymentController {}
class DatabaseConnectionManager {}

// Avoid
class userservice {}
class paymentCtrl {}
class DBConnMgr {}

Interfaces should also use PascalCase, with a descriptive name that indicates the contract:

// Good
interface User {
  id: string
  email: string
}

interface PaymentGateway {
  processPayment(amount: number): Promise<PaymentResult>
}

// Avoid - Hungarian notation is outdated
interface IUser {}
interface IPaymentGateway {}

Methods and Properties

Use camelCase for methods and properties, with names that clearly indicate their purpose:

// Good
class UserService {
  async findUserById(id: string): Promise<User> {}
  async updateUserProfile(userId: string, data: UpdateUserDto): Promise<User> {}

  private validateEmailFormat(email: string): boolean {}
}

// Avoid
class UserService {
  async getUser(id: string) {} // Too generic
  async upd(id: string, d: any) {} // Abbreviated and unclear
}

Angular Component Naming Patterns

Angular's architecture provides specific guidance for naming different types of components and services. Following these conventions makes your application more maintainable and helps other Angular developers understand your code structure immediately. We applied these patterns extensively when building our SoarUI design system and across large-scale Angular applications.

Component Classes and Files

Angular components should use PascalCase for the class name and kebab-case for file names:

// file: user-profile.component.ts
@Component({
  selector: 'app-user-profile',
  templateUrl: './user-profile.component.html',
})
export class UserProfileComponent {
  // Component logic
}

// file: payment-history-card.component.ts
@Component({
  selector: 'app-payment-history-card',
  templateUrl: './payment-history-card.component.html',
})
export class PaymentHistoryCardComponent {
  // Component logic
}

Services and Providers

Services should have descriptive names that indicate their responsibility:

// Good - Clear responsibility
@Injectable()
export class UserAuthenticationService {
  login(credentials: LoginCredentials): Observable<AuthResult> {}
  logout(): void {}
  refreshToken(): Observable<string> {}
}

@Injectable()
export class PaymentProcessingService {
  processPayment(request: PaymentRequest): Observable<PaymentResult> {}
  validateCard(cardNumber: string): boolean {}
}

// Avoid - Too generic
@Injectable()
export class DataService {}

@Injectable()
export class UtilService {}

Directives and Pipes

Custom directives and pipes should have clear, action-oriented names:

// Directive - describes what it does
@Directive({
  selector: '[appHighlightOnHover]',
})
export class HighlightOnHoverDirective {}

// Pipe - describes the transformation
@Pipe({ name: 'formatCurrency' })
export class FormatCurrencyPipe {
  transform(value: number, currency: string): string {}
}

NestJS Module and Service Naming

NestJS follows similar conventions but has its own architectural patterns that influence naming decisions.

Controllers

Controllers should be named after the resource they manage, with clear HTTP method mappings:

// Good
@Controller('users')
export class UsersController {
  @Get(':id')
  async findUserById(@Param('id') id: string): Promise<UserResponseDto> {}

  @Post()
  async createUser(@Body() createUserDto: CreateUserDto): Promise<UserResponseDto> {}

  @Put(':id/profile')
  async updateUserProfile(
    @Param('id') id: string,
    @Body() updateProfileDto: UpdateProfileDto
  ): Promise<UserResponseDto> {}
}

// Avoid
@Controller('api/v1/user-management-endpoints')
export class UserMgmtCtrl {}

Services and Repositories

Services should have names that clearly indicate their business domain:

// Good - Domain-specific services
@Injectable()
export class UserAccountService {
  async createAccount(userData: CreateAccountDto): Promise<User> {}
  async deactivateAccount(userId: string): Promise<void> {}
}

@Injectable()
export class PaymentBillingService {
  async calculateMonthlyBill(userId: string): Promise<BillingAmount> {}
  async processRecurringPayment(subscriptionId: string): Promise<PaymentResult> {}
}

// Repository pattern
@Injectable()
export class UserRepository {
  async findById(id: string): Promise<User | null> {}
  async save(user: User): Promise<User> {}
  async deleteById(id: string): Promise<void> {}
}

DTOs and Entity Classes

Data Transfer Objects and Entity classes should clearly indicate their purpose:

// Good - Clear purpose and direction
export class CreateUserDto {
  email: string
  password: string
  firstName: string
  lastName: string
}

export class UserResponseDto {
  id: string
  email: string
  firstName: string
  lastName: string
  createdAt: Date
  // Note: password is never included in response DTOs
}

export class UpdateUserProfileDto {
  firstName?: string
  lastName?: string
  phoneNumber?: string
}

// Entity
@Entity('users')
export class User {
  @PrimaryGeneratedColumn('uuid')
  id: string

  @Column({ unique: true })
  email: string

  @Column()
  hashedPassword: string
}

Advanced Naming Strategies

Generic Type Parameters

Use meaningful names for generic type parameters, especially in complex scenarios:

// Good - Descriptive generic names
interface Repository<TEntity, TKey> {
  findById(id: TKey): Promise<TEntity | null>
  save(entity: TEntity): Promise<TEntity>
}

interface ApiResponse<TData, TError = ApiError> {
  data?: TData
  error?: TError
  timestamp: Date
}

// Single letter for simple cases
interface List<T> {
  items: T[]
  length: number
}

Event and Callback Naming

Use consistent patterns for events and callbacks:

// Angular - Event emitters
@Component({})
export class UserProfileComponent {
  @Output() userUpdated = new EventEmitter<User>()
  @Output() profileDeleted = new EventEmitter<string>() // userId

  onSaveProfile(): void {
    // Handle save logic
    this.userUpdated.emit(this.user)
  }
}

// NestJS - Event handlers
@Injectable()
export class UserEventHandler {
  @OnEvent('user.registered')
  handleUserRegistered(payload: UserRegisteredEvent): void {}

  @OnEvent('payment.processed')
  handlePaymentProcessed(payload: PaymentProcessedEvent): void {}
}

Team Conventions and Tooling

Establishing Team Standards

Document your naming conventions in a style guide that covers:

  • File and folder naming patterns
  • Class, interface, and method naming rules
  • Specific patterns for your domain (e.g., financial services, e-commerce)
  • Abbreviation guidelines and approved acronyms

Automated Enforcement

Use ESLint rules to enforce naming conventions:

// .eslintrc.json
{
  "rules": {
    "@typescript-eslint/naming-convention": [
      "error",
      {
        "selector": "class",
        "format": ["PascalCase"]
      },
      {
        "selector": "interface",
        "format": ["PascalCase"]
      },
      {
        "selector": "method",
        "format": ["camelCase"]
      },
      {
        "selector": "property",
        "format": ["camelCase"]
      }
    ]
  }
}

Code Review Checklist

Include naming convention checks in your code review process:

  • Are class and interface names descriptive and follow PascalCase?
  • Do method names clearly indicate their action and follow camelCase?
  • Are DTOs properly named with their direction (Create, Update, Response)?
  • Do file names match the class names and follow framework conventions?

Make names work for your team

Consistent naming conventions pay dividends over time. New team members onboard faster, bugs from misunderstood component responsibilities decrease, and IDEs provide better autocomplete and refactoring support. The investment in establishing these patterns early pays for itself many times over as the application grows.

Code is read far more often than it is written. Choose names that will make sense to your future self and your teammates six months from now. If you need help establishing these conventions across a growing codebase, we can help.

Building something with Angular?

From component libraries to full product builds, we've shipped Angular at scale across dozens of projects.

More articles

Running a Consultancy on Open-Source Business Tools: Our Operations Playbook

How Exceev runs its business operations on Twenty CRM, ZeroMail, n8n automation, Ghost publishing, Cal.com scheduling, and Postiz social publishing. An operations playbook for consultancies that want control over their business stack.

Read more

Self-Hosting Our Infrastructure: The Observability, Security, and Deployment Stack

How Exceev self-hosts its infrastructure with Grafana, Prometheus, Loki, k6, Coolify, Infisical, Docker, Tailscale, Cloudflared, Beszel, and Duplicati. An operational deep dive into observability, deployment, security, and resilience.

Read more

Tell us about your project

Our offices

  • Exceev Consulting
    61 Rue de Lyon
    75012, Paris, France
  • Exceev Technology
    332 Bd Brahim Roudani
    20330, Casablanca, Morocco