Best Practices Angular - JU-DEV-Bootcamps/ERAS GitHub Wiki
This document outlines the best practices and coding standards to ensure consistency, maintainability, and scalability in our Angular 19 project. It covers folder structure, Angular CLI usage, modular architecture, and other key guidelines for writing clean, efficient code.
A consistent folder structure improves project organization and collaboration. Arrange your files logically by separating components, services, modules, and other resources into dedicated directories.
/src
/app
/components
/header
header.component.ts
header.component.html
header.component.css
/services
data.service.ts
/modules
main.module.ts
Leverage Angular CLI to generate components, services, modules, and more. It helps enforce best practices, reduces human error, and standardizes project setup.
ng new my-angular-app
ng generate component header
ng generate service data
Break your application into smaller, reusable modules. Each module should serve a well-defined purpose and responsibility.
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, SharedModule, UserModule],
bootstrap: [AppComponent]
})
export class AppModule {}
Ensure each component or service has a single responsibility. This makes it easier to maintain, test, and extend the code.
Bad Example:
@Component({
selector: 'app-user',
templateUrl: './user.component.html'
})
export class UserComponent {
fetchUserData() { ... }
displayUserData() { ... }
updateUserData() { ... }
}
Good Example:
@Component({
selector: 'app-user',
templateUrl: './user.component.html'
})
export class UserComponent {
displayUserData() { ... }
}
Use descriptive, consistent names for files, classes, variables, and functions to enhance code readability.
Bad Example:
const x = 10;
Good Example:
const itemCount = 10;
Organize components with a consistent structure that includes separate files for templates, styles, and TypeScript logic.
/header
header.component.ts
header.component.html
header.component.css
Enable strict mode for better type-checking, linting, and overall code quality. This is enabled by default in Angular 19.
{
"angularCompilerOptions": {
"strict": true
}
}
Leverage standalone components where appropriate to avoid unnecessary modules. This improves modularity and reduces complexity.
@Component({
selector: 'app-header',
standalone: true,
imports: [CommonModule],
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
export class HeaderComponent {}
Follow best practices when using RxJS to manage asynchronous operations. Always unsubscribe when necessary and use appropriate operators.
Ensure consistent code formatting and style by using ESLint and Prettier. Below is a configuration example:
ESLint Configuration:
{
"extends": ["eslint:recommended", "plugin:@angular-eslint/recommended"]
}
Prettier Configuration:
{
"singleQuote": true,
"trailingComma": "es5"
}
Regularly update dependencies to ensure compatibility with Angular 19. Verify all dependencies, such as TypeScript and RxJS, are compatible with the latest Angular version.
npm install [email protected] --save-dev
It's important to ensure that all developers are using the same version of Node.js to avoid discrepancies between environments. To manage Node versions effectively, we recommend using nvm (Node Version Manager). This allows each developer to easily switch between Node versions for different projects and ensures consistency across the team.
-
Install nvm: Follow the instructions from the official nvm repository to install it on your machine.
-
Install Node.js: Once nvm is installed, you can install the required Node version for the project (for example, Node 18.x, which is recommended for Angular 19).
nvm install 18
-
Use the Installed Version: Set the installed version as the default for your terminal sessions.
nvm use 18
-
Check the Node Version: Verify the Node version you're using with:
node -v
-
Setting Default Version: You can set a default version of Node.js for all future terminal sessions using:
nvm alias default 18
By using nvm, we ensure that everyone on the team uses the correct version of Node.js that is compatible with the Angular 19 ecosystem and our project's dependencies.
Enable strict type-checking options to catch more errors at compile time.
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true
}
}
Ensure consistent module resolution.
{
"compilerOptions": {
"moduleResolution": "node",
"baseUrl": "./",
"paths": {
"@app/*": ["src/app/*"]
}
}
}
Specify the ECMAScript target and library.
{
"compilerOptions": {
"target": "es2015",
"lib": ["es2015", "dom"]
}
}
Enforce type annotations for function parameters and return types.
{
"@typescript-eslint/explicit-module-boundary-types": "error",
"@typescript-eslint/explicit-function-return-type": ["error", { "allowExpressions": true }]
}
Disallow the use of implicit any
types.
{
"@typescript-eslint/no-explicit-any": "error"
}
Disallow unused variables and parameters.
{
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }]
}
Enforce consistent naming conventions for interfaces and types.
{
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "interface",
"format": ["PascalCase"],
"custom": {
"regex": "^I[A-Z]",
"match": false
}
}
]
}
Define the shape of objects using interfaces.
interface User {
id: number;
name: string;
email: string;
}
Use enums to define a set of named constants.
enum UserRole {
Admin,
Editor,
Viewer
}
Use type guards to narrow down types.
function isUser(obj: any): obj is User {
return obj && typeof obj.id === 'number' && typeof obj.name === 'string';
}
Use generics to create reusable components.
function identity<T>(arg: T): T {
return arg;
}
Utilize built-in utility types like Partial
, Readonly
, Pick
, and Omit
.
type PartialUser = Partial<User>;
type ReadonlyUser = Readonly<User>;
Use conditional types to define types based on conditions.
type MessageOf<T> = T extends { message: infer MessageType } ? MessageType : never;
Use mapped types to create new types based on existing types.
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean;
};
This structured wiki entry should help your development team understand and apply the TypeScript rules effectively in your Angular project.