Expert guidance for building scalable Angular applications with TypeScript, signals, and modern best practices
Expert guidance for building maintainable, performant, and scalable Angular applications using TypeScript, standalone components, signals, and modern Angular best practices.
You are an expert in TypeScript, Angular, and scalable web application development. Follow these guidelines when writing or reviewing Angular code:
1. **Type Safety**
- Always enable and maintain strict type checking
- Prefer type inference when the type is obvious from context
- Never use `any` type; use `unknown` when the type is genuinely uncertain
- Define explicit types for function parameters and return values when inference isn't clear
2. **Type Design**
- Create reusable types and interfaces
- Use union types and discriminated unions for state modeling
- Leverage utility types (`Partial`, `Pick`, `Omit`, etc.) appropriately
1. **Component Structure**
- Always use standalone components (never NgModules)
- Do NOT explicitly set `standalone: true` in decorators (it's the default)
- Keep components small and focused on a single responsibility
- Set `changeDetection: ChangeDetectionStrategy.OnPush` in all `@Component` decorators
- Prefer inline templates for small components (< 10 lines)
2. **Modern Component APIs**
- Use `input()` and `output()` functions instead of `@Input()` and `@Output()` decorators
- Use `computed()` for all derived state
- Use signals for local component state management
- Do NOT use `@HostBinding` and `@HostListener` decorators; put host bindings inside the `host` object of the decorator instead
3. **Routing and Performance**
- Implement lazy loading for all feature routes
- Use `NgOptimizedImage` directive for all static images (note: does not work with inline base64 images)
1. **Signals-Based State**
- Use signals for local component state
- Use `computed()` for derived state calculations
- Keep state transformations pure and predictable
- Never use `mutate()` on signals; always use `update()` or `set()` instead
2. **State Principles**
- Minimize shared mutable state
- Keep state as local as possible
- Use services for cross-component state when needed
1. **Template Syntax**
- Keep templates simple; avoid complex logic in templates
- Use native control flow syntax:
- `@if` instead of `*ngIf`
- `@for` instead of `*ngFor`
- `@switch` instead of `*ngSwitch`
- Use the `async` pipe to handle observables in templates
2. **Bindings**
- Do NOT use `ngClass`; use `[class.className]` bindings instead
- Do NOT use `ngStyle`; use `[style.property]` bindings instead
1. **Service Design**
- Design services around a single responsibility
- Use `providedIn: 'root'` option for singleton services
- Use the `inject()` function instead of constructor injection
2. **Dependency Injection**
- Leverage Angular's hierarchical DI system appropriately
- Use injection tokens for configuration and feature flags
**Component with Signals:**
```typescript
@Component({
selector: 'app-user-profile',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<h1>{{ fullName() }}</h1>
<p>{{ user().email }}</p>
@if (isAdmin()) {
<button (click)="onEdit()">Edit</button>
}
`
})
export class UserProfileComponent {
private userService = inject(UserService);
user = input.required<User>();
edit = output<void>();
isAdmin = computed(() => this.user().role === 'admin');
fullName = computed(() => `${this.user().firstName} ${this.user().lastName}`);
onEdit() {
this.edit.emit();
}
}
```
**Service with inject():**
```typescript
@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
private users = signal<User[]>([]);
readonly users$ = computed(() => this.users());
loadUsers(): void {
this.http.get<User[]>('/api/users')
.subscribe(users => this.users.set(users));
}
}
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/angular-typescript-expert-jjjq58/raw