# Security Best Practices Standards for TypeScript
This document outlines security best practices for TypeScript development. These standards are designed to help developers write secure, maintainable, and performant code. By adhering to these guidelines, we can minimize vulnerabilities and create more robust applications.
## 1. Input Validation and Sanitization
### Standard
Always validate and sanitize user input on both the client and server sides.
**Why:** Preventing common injection attacks (XSS, SQLi, command injection).
**Do This:**
* Use validation libraries to enforce expected formats and constraints.
* Sanitize input to remove or escape potentially harmful characters.
* Implement allow-lists rather than deny-lists for predictable security.
**Don't Do This:**
* Trust user input without validation.
* Rely solely on client-side validation, as it can be bypassed.
* Use regular expressions as the *only* validation step, especially regexes constructed dynamically.
**Code Examples:**
TypeScript function to validate and sanitize user input:
"""typescript
/**
* Validates email format and sanitizes it to prevent XSS attacks.
* @param email The email address to validate and santize
* @returns The sanitized email if valid, null otherwise
*/
function validateAndSanitizeEmail(email: string): string | null {
if (typeof email !== 'string') {
return null; // Ensure it's a string
}
// Basic email format validation using regex, can be replaced with more advanced libraries.
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return null; // Invalid email format
}
// Sanitize email to prevent XSS: replace <, >, and "
const sanitizedEmail = email.replace(//g, '>').replace(/"/g, '"');
return sanitizedEmail;
}
// Usage example
const userInputEmail = 'test@example.com';
const sanitized = validateAndSanitizeEmail(userInputEmail);
if (sanitized) {
console.log("Valid and Sanitized email: ", sanitized); // Outputs: Valid and Sanitized email: <script>alert("XSS")</script>test@example.com
} else {
console.error("Invalid Email");
}
"""
**Anti-Pattern:**
"""typescript
// Vulnerable code - no input validation or sanitization
function displayUserInput(input: string): void {
document.getElementById('output').innerHTML = input; // Prone to XSS
}
displayUserInput(userInput);
"""
## 2. Authentication and Authorization
### Standard
Implement robust authentication and authorization mechanisms, avoiding common pitfalls.
**Why:** Protecting sensitive data and resources from unauthorized access.
**Do This:**
* Use strong password hashing algorithms (e.g., bcrypt or Argon2).
* Implement multi-factor authentication (MFA) where possible/necessary.
* Apply the principle of least privilege (POLP) for user roles and permissions.
* Use established libraries that support best-practice security measures.
**Don't Do This:**
* Store passwords in plaintext or using weak hashing algorithms (e.g., MD5, SHA1).
* Grant excessive permissions to users or roles.
* Rely solely on cookies for authentication.
**Code Examples:**
Password hashing with bcrypt:
"""typescript
import bcrypt from 'bcrypt';
async function hashPassword(password: string): Promise {
const saltRounds = 10; // Number of salt rounds impacts security and performance.
const hashedPassword = await bcrypt.hash(password, saltRounds);
return hashedPassword;
}
async function comparePassword(password: string, hash: string): Promise {
return await bcrypt.compare(password, hash);
}
// Example usage:
async function registerUser(password: string) {
const hashedPassword = await hashPassword(password);
console.log("Hashed password:", hashedPassword);
// Store the hashed password in the database
}
async function loginUser(password: string, hashedPasswordFromDB: string) {
const passwordMatch = await comparePassword(password, hashedPasswordFromDB);
if (passwordMatch) {
console.log("Login Successful");
} else {
console.log("Login Failed");
}
}
//Example calls. Never store passwords in memory like this!
const examplePassword = "StrongPassword123!";
registerUser(examplePassword);
//Pretend we retrieve this from the database
const hashedPasswordFromDB = "$2b$10$IWD5R8X3AAvbZzV2L5l51eG99.W6eO8B5s5F4h4k9.8wzJ8eO6t2";
loginUser(examplePassword, hashedPasswordFromDB);
"""
Authorization middleware function for Express:
"""typescript
import { Request, Response, NextFunction } from 'express';
interface CustomRequest extends Request {
user?: { role: string }; // Extend Request type to include user info
}
function authorize(role: string) {
return (req: CustomRequest, res: Response, next: NextFunction) => {
if (!req.user || req.user.role !== role) {
return res.status(403).json({ message: 'Unauthorized' }); // 403 Forbidden
}
next();
};
}
// Usage in an Express route:
// app.get('/admin', authenticate, authorize('admin'), (req: Request, res: Response) => {
// res.json({ message: 'Admin access granted' });
// });
"""
**Anti-Pattern:**
"""typescript
// Vulnerable code - storing passwords in plaintext!
function createUser(username: string, password: string): void {
// BAD PRACTICE: Never store passwords in plain text.
// Storing passwords wihout hashing is extremely dangerous.
console.log("Creating user ${username} with password ${password}");
}
createUser('testUser', 'insecurePassword');
"""
### Technology Specific Details
* **JWT (JSON Web Tokens):** When working with JWTs, always verify the signature server-side using a secret key that is securely stored and rotated regularly. Ensure that you are using the "jsonwebtoken" library correctly to prevent tampering or replay attacks.
* **OAuth 2.0:** Properly configure your OAuth 2.0 flows. Ensure redirect URIs are validated to prevent authorization code injection. Always use PKCE (Proof Key for Code Exchange) for public clients (e.g., mobile and browser-based apps).
* **CORS (Cross-Origin Resource Sharing):** Configure CORS carefully. Only allow specific and known origins, and avoid wildcard settings ("*"). Implement proper preflight request handling.
## 3. Data Protection
### Standard
Protect sensitive data both in transit and at rest.
**Why:** Preventing data breaches and maintaining compliance with privacy regulations.
**Do This:**
* Use HTTPS for all network communication.
* Encrypt sensitive data at rest (e.g., database fields, files).
* Implement encryption algorithms that adhere to industry best practices.
* Use environment variables and secrets management for sensitive credentials.
**Don't Do This:**
* Transmit sensitive data over unencrypted channels (HTTP).
* Store sensitive data in plaintext in databases or configuration files.
* Embed secrets directly in code.
**Code Examples:**
HTTPS enforcement in Express:
"""typescript
import express, { Request, Response, NextFunction } from 'express';
import helmet from 'helmet';
const app = express();
// Force HTTPS using helmet
app.use(helmet({
hsts: {
maxAge: 31536000, // One year in seconds
includeSubDomains: true,
preload: true
},
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:"],
upgradeInsecureRequests: [],
},
},
}));
//Middleware to redirect HTTP to HTTPS if not already secured. Avoids infinite redirect loops in properly configured environments
app.use((req: Request, res: Response, next: NextFunction) => {
if (req.secure) {
// request was via https, so do no special handling
next();
} else {
// request was via http, so redirect to https
res.redirect('https://' + req.headers.host + req.url);
}
});
app.get('/', (req: Request, res: Response) => {
res.send('Hello, world!');
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log("Server is running on port ${port}");
});
"""
Example of encrypting data at rest using the "crypto" library:
"""typescript
import crypto from 'crypto';
const algorithm = 'aes-256-cbc'; // Choose a strong encryption algorithm
const key = crypto.randomBytes(32); // Generate a secure encryption key
const iv = crypto.randomBytes(16); // Initialization vector
/**
* Encrypts text using AES-256-CBC with a provided key and IV.
* @param text The text to be encrypted.
* @returns An object containing the encrypted data, IV, and encryption key. In a real environment, the key would be managed independently and not be present in the return result.
*/
function encrypt(text: string) {
const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), iv);
let encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return { iv: iv.toString('hex'), encryptedData: encrypted.toString('hex'), encryptionKey: key.toString('hex') }; //NEVER RETURN THE KEY AND IV LIKE THIS IN PRODUCTION. This is for demonstration purposes only
}
/**
* Decrypts the encrypted data using the provided IV and encryption key
* @param ivString The initialization vector
* @param encryptedDataString The encrypted data.
* @returns The decrypted text.
*/
function decrypt(ivString: string, encryptedDataString: string, encryptionKey: string) {
const iv = Buffer.from(ivString, 'hex');
const encryptedText = Buffer.from(encryptedDataString, 'hex');
const decipher = crypto.createDecipheriv(algorithm, Buffer.from(encryptionKey, 'hex'), iv);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
}
const text = 'Sensitive data to be encrypted';
const encryptionResult = encrypt(text);
// NEVER store the encryption key and IV together with the encrypted Data.
//This is for demonstration purposes only
const ivReturned = encryptionResult.iv;
const encryptedDataReturned = encryptionResult.encryptedData;
const keyReturned = encryptionResult.encryptionKey;
console.log("IV: ", ivReturned);
console.log("Encrypted Data: ", encryptedDataReturned);
const decryptedText = decrypt(ivReturned, encryptedDataReturned, keyReturned);
console.log("Decrypted Text: ", decryptedText); //Logs "Sensitive data to be encrypted"
"""
**Anti-Pattern:**
"""typescript
// Vulnerable code - storing API keys directly in the code
const apiKey = 'YOUR_API_KEY'; // UNSAFE: Never store secrets directly in the source code. This is particularly dangerous if the respository is public!
"""
## 4. Dependency Management
### Standard
Regularly review and update dependencies, addressing known vulnerabilities.
**Why:** Mitigating risks from vulnerable third-party libraries and frameworks.
**Do This:**
* Use a dependency management tool (e.g., npm, yarn, pnpm).
* Regularly run security audits (e.g., "npm audit", "yarn audit").
* Update dependencies to the latest stable versions.
* Use tools like Snyk, Dependabot, or similar that automate vulnerability scanning, dependency updates and automatically create pull requests.
* Consider using a Software Bill of Materials tool (SBOM) to maintain an inventory of all software components and their dependencies. Examples tools include Syft, Grype, or CycloneDX.
**Don't Do This:**
* Use outdated or unmaintained dependencies.
* Ignore security audit warnings.
* Install dependencies from untrusted sources.
**Code Examples:**
Running a security audit with npm:
"""bash
npm audit
"""
Updating vulnerable dependencies:
"""bash
npm update
"""
Using "npm audit fix":
"""bash
npm audit fix //Tries to automatically fix known vulnerabilities -- careful with breaking changes and test thoroughly!
"""
**Anti-Pattern:**
"""typescript
// Vulnerable code - using outdated library without security updates
import $ from 'jquery'; // Using an old, vulnerable version of jQuery
"""
### Technology Specific Details
* **Lockfiles**: Always commit your lockfiles ("package-lock.json", "yarn.lock", "pnpm-lock.yaml") to your repository. Lockfiles ensure that everyone on the team is using the exact same versions of dependencies, preventing issues caused by automatically updating minor versions.
* **"npm ci" command**: Use the "npm ci" command in your CI/CD pipeline. "npm ci" installs dependencies from the "package-lock.json" file, ensuring a clean and reproducible build. It's significantly faster and more secure because it doesn't update the lock file.
* **Subresource Integrity (SRI)**: When using CDNs, use SRI to ensure that the files you load from the CDN have not been tampered with. Generate the SRI hash and include it in your ""
* **Scope your packages**: Use npm's scoped packages to prevent naming conflicts and increase security. Scopes are particularly important for proprietary or private packages.
* **Consider supply chain security**: Investigate and implement measures to prevent supply chain attacks, such as verifying package authenticity and using a private npm registry.
## 5. Error Handling and Logging
### Standard
Implement proper error handling and logging mechanisms, avoiding information leaks.
**Why:** Preventing sensitive information from being exposed in error messages or logs.
**Do This:**
* Implement centralized error handling.
* Log errors and exceptions, but redact sensitive data.
* Use structured logging for easier analysis.
* Monitor logs for suspicious activity.
**Don't Do This:**
* Expose stack traces or sensitive data in error messages to end users.
* Log sensitive data in production logs.
* Ignore errors and exceptions. Catch and handle exceptions using try-catch blocks.
**Code Examples:**
Centralized error handling in Express:
"""typescript
import express, { Request, Response, NextFunction } from 'express';
const app = express();
app.get('/', (req: Request, res: Response, next: NextFunction) => {
try {
// Simulate an error
throw new Error('Simulated error');
} catch (error) {
next(error); // Pass error to error handling middleware
}
});
// Error handling middleware
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.error(err.stack); // Log the error stack trace (redact sensitive data)
res.status(500).send('Something went wrong!'); // Generic error message to the user
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
"""
Structured logging with Winston:
"""typescript
import winston from 'winston';
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
defaultMeta: { service: 'user-service' },
transports: [
new winston.transports.Console({
format: winston.format.simple(),
}),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
],
});
// Example usage:
// logger.info('User logged in', { userId: 123 });
// logger.error('Failed to process payment', { error: 'Invalid card', userId: 123 });
"""
**Anti-Pattern:**
"""typescript
// Vulnerable code - exposing stack traces to the user
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
// BAD PRACTICE: Exposing full stack traces to users can reveal sensitive information about the server's internal workings. Don't do this in production.
res.status(500).send(err.stack);
});
"""
## 6. Code Reviews and Static Analysis
### Standard
Conduct thorough code reviews and use static analysis tools to identify potential vulnerabilities.
**Why:** Detecting security issues early in the development lifecycle.
**Do This:**
* Implement a code review process involving multiple developers.
* Use static analysis tools to automatically scan for security vulnerabilities.
* Integrate security checks into the CI/CD pipeline.
**Don't Do This:**
* Skip code reviews.
* Ignore static analysis warnings.
* Deploy code without security checks.
**Code Examples:**
Example using ESLint with security-related plugins:
"""javascript
// .eslintrc.js
module.exports = {
"plugins": [
"security",
"no-secrets"
],
"rules": {
"security/detect-unsafe-regex": "warn",
"security/detect-eval-with-expression": "warn",
"security/detect-pseudoRandomBytes": "warn",
"no-secrets/no-secrets": "error"
}
};
"""
Run ESLint with the security plugin:
"""bash
eslint .
"""
Example with SonarQube
"""bash
// Perform a SonarQube scan
sonar-scanner -Dsonar.projectKey=my-project -Dsonar.sources=. -Dsonar.host.url=http://localhost:9000 -Dsonar.login=your_token
"""
**Anti-Pattern:**
"""typescript
// Vulnerable code - deploying code without review or static analysis
function deployCode(code: string): void {
// Unsafe: Deploying code without proper review can lead to vulnerabilities
console.log('Deploying code:', code);
}
deployCode('Unsafe Code');
"""
## 7. Session Management
### Standard
Implement secure session management to prevent session hijacking and fixation attacks.
**Why:** Protecting user sessions from unauthorized access.
**Do This:**
* Use cryptographically secure session IDs.
* Rotate session IDs after login.
* Set appropriate session timeouts.
* Store session data securely.
**Don't Do This:**
* Use predictable session IDs.
* Store sensitive data in session cookies.
* Use long session timeouts without appropriate measures.
**Code Examples:**
Session management with Express and "express-session":
"""typescript
import express, { Request, Response } from 'express';
import session from 'express-session';
import crypto from 'crypto';
const app = express();
// Generate a random secret for session encryption. In a real environment, this would be securely stored and rotated regularly.
const sessionSecret = crypto.randomBytes(32).toString('hex');
app.use(session({
secret: sessionSecret, // Use a strong, randomly generated secret
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // Serve only over HTTPS in production
httpOnly: true, // Prevent client-side JavaScript access
maxAge: 60 * 60 * 1000 // Session timeout of 1 hour
}
}));
app.get('/', (req: Request, res: Response) => {
req.session.views = (req.session.views || 0) + 1;
res.send("You have viewed this page ${req.session.views} times");
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
"""
Rotating session ID after login:
"""typescript
app.post('/login', (req: Request, res: Response) => {
// Validate user credentials
if (isValidUser(req.body.username, req.body.password)) {
req.session.regenerate((err) => { // Regenerate session ID after successful login
if (err) {
console.log(err);
}
req.session.user = { username: req.body.username };
res.send('Login successful');
});
} else {
res.status(401).send('Invalid credentials');
}
});
"""
**Anti-Pattern:**
"""typescript
// Vulnerable code - using a weak session secret
app.use(session({
secret: 'insecureSecret', // BAD: Use a strong, randomly generated secret. hardcoded secrets shouldn't be used.
resave: false,
saveUninitialized: true
}));
"""
## 8. Cross-Site Scripting (XSS) Prevention
### Standard
Employ techniques to prevent XSS attacks, such as output encoding and Content Security Policy (CSP).
**Why:** Preventing malicious scripts from being injected into web pages.
**Do This:**
* Encode output based on context (HTML, JavaScript, URL).
* Use a templating engine that automatically handles output encoding.
* Implement Content Security Policy (CSP) to restrict the sources of content.
**Don't Do This:**
* Insert user-controlled data directly into HTML without encoding.
* Disable CSP without a strong justification.
**Code Examples:**
Output encoding using a templating engine (e.g., Handlebars):
"""html
// Handlebars template with automatic output encoding. Most modern template systems like React, Angular, Vue, Jinja, and others employ measures to guard against XSS by default.
<p>Hello, {{name}}!</p>
"""
Implementing CSP using Helmet:
"""typescript
import express from 'express';
import helmet from 'helmet';
const app = express();
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:"],
upgradeInsecureRequests: [],
},
}));
app.get('/', (req, res) => {
res.send('Hello, world!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
"""
**Anti-Pattern:**
"""typescript
// Vulnerable code - inserting user input directly into HTML
function displayUserInput(input: string): void {
document.getElementById('output').innerHTML = input; // Vulnerable to XSS
}
"""
## 9. Cross-Site Request Forgery (CSRF) Prevention
### Standard
Implement CSRF protection mechanisms to prevent malicious websites from performing unauthorized actions on behalf of authenticated users.
**Why:** Protecting against unauthorized actions triggered by cross-site requests.
**Do This:**
* Use anti-CSRF tokens for all state-changing requests (e.g., POST, PUT, DELETE).
* Validate the Origin or Referer header on the server-side.
* Use the SameSite cookie attribute to mitigate CSRF attacks.
**Don't Do This:**
* Rely solely on cookies for authentication, without CSRF protection.
* Disable CSRF protection without understanding the risks.
**Code Examples:**
CSRF protection with Express and "csurf":
"""typescript
import express, { Request, Response } from 'express';
import csurf from 'csurf';
import cookieParser from 'cookie-parser';
const app = express();
app.use(cookieParser());
const csrfProtection = csurf({
cookie: true
});
app.use(csrfProtection);
app.get('/form', (req: Request, res: Response) => {
// pass the csrfToken to the view
res.send("
Submit
");
});
app.post('/process', csrfProtection, (req: Request, res: Response) => {
res.send('Form processed successfully!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
"""
**Anti-Pattern:**
"""typescript
// Vulnerable code - not using CSRF protection
app.post('/transfer', (req: Request, res: Response) => {
// Perform money transfer without CSRF protection
// This is vulnerable to CSRF attacks
});
"""
## 10. Rate Limiting
### Standard
Implement rate limiting to protect against brute-force attacks and resource exhaustion.
**Why:** Preventing abuse of resources and maintaining service availability.
**Do This:**
* Implement rate limiting middleware.
* Configure rate limits based on the sensitivity of the endpoint.
* Use different rate limits for authenticated and unauthenticated users.
**Don't Do This:**
* Expose endpoints without any rate limiting.
* Set overly generous rate limits.
**Code Examples:**
Rate limiting with Express and "express-rate-limit":
"""typescript
import express from 'express';
import rateLimit from 'express-rate-limit';
const app = express();
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP, please try again after 15 minutes',
standardHeaders: true, // Return rate limit info in the "RateLimit-*" headers
legacyHeaders: false, // Disable the "X-RateLimit-*" headers
});
// Apply the rate limiting middleware to all requests
app.use(limiter);
app.get('/', (req, res) => {
res.send('Hello, world!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
"""
**Anti-Pattern:**
"""typescript
// Vulnerable code - no rate limiting
app.post('/login', (req: Request, res: Response) => {
// Process login attempt without rate limiting
// This is vulnerable to brute-force attacks
});
"""
## 11. TypeScript Specific Security Considerations
### Standard
Leverage TypeScript features to enhance security and prevent common errors.
**Why:** TypeScript's strong typing system and compile-time checks can help catch vulnerabilities early. Enums, Readonly properties and interfaces should be leveraged for security of systems.
**Do This:**
* Use strict mode (""strict": true" in "tsconfig.json").
* Utilize "readonly" to prevent accidental modification of sensitive data.
* Define clear interfaces to enforce data structures.
* Use type guards to narrow types and prevent unexpected behavior.
* Prefer "enum" over string literals for known, finite sets of values.
**Don't Do This:**
* Disable strict mode.
* Rely on "any" type excessively.
* Ignore TypeScript compiler errors.
**Code Examples:**
Using "readonly" for preventing data modification:
"""typescript
interface User {
readonly id: string;
name: string;
}
const user: User = {
id: '123',
name: 'John Doe',
};
// user.id = '456'; // Compile-time error: Cannot assign to 'id' because it is a read-only property.
user.name = 'Jane Doe'; // Allowed
"""
### Technology Specific Details
* **"--noImplicitAny"**: Always enable the "--noImplicitAny" compiler option in your "tsconfig.json". This flag ensures that you explicitly type every variable, reducing the risk of unexpected "any" types creeping into your code.
* **"--strictNullChecks"**: Enable "--strictNullChecks" to prevent null and undefined errors. This helps catch potential runtime exceptions early.
* **Use discriminated unions**: Discriminated unions(tagged unions) are useful for creating safer and more predictable code. They force you to handle all possible cases, reducing the risk of runtime errors.
* **Consider using Zod or Yup**: These libraries allow you to define schemas and validate data at runtime, providing an extra layer of security against unexpected data.
**Anti-Pattern:**
"""typescript
// Vulnerable code - using "any" type excessively
function processData(data: any): void {
console.log(data.someProperty); // No type checking, potential runtime error
}
"""
These standards provide a foundation for developing secure TypeScript applications. Regular training, code reviews, and updates to these standards are crucial to adapt to evolving security threats.
danielsogl
Created Mar 6, 2025
This guide explains how to effectively use .clinerules
with Cline, the AI-powered coding assistant.
The .clinerules
file is a powerful configuration file that helps Cline understand your project's requirements, coding standards, and constraints. When placed in your project's root directory, it automatically guides Cline's behavior and ensures consistency across your codebase.
Place the .clinerules
file in your project's root directory. Cline automatically detects and follows these rules for all files within the project.
# Project Overview project: name: 'Your Project Name' description: 'Brief project description' stack: - technology: 'Framework/Language' version: 'X.Y.Z' - technology: 'Database' version: 'X.Y.Z'
# Code Standards standards: style: - 'Use consistent indentation (2 spaces)' - 'Follow language-specific naming conventions' documentation: - 'Include JSDoc comments for all functions' - 'Maintain up-to-date README files' testing: - 'Write unit tests for all new features' - 'Maintain minimum 80% code coverage'
# Security Guidelines security: authentication: - 'Implement proper token validation' - 'Use environment variables for secrets' dataProtection: - 'Sanitize all user inputs' - 'Implement proper error handling'
Be Specific
Maintain Organization
Regular Updates
# Common Patterns Example patterns: components: - pattern: 'Use functional components by default' - pattern: 'Implement error boundaries for component trees' stateManagement: - pattern: 'Use React Query for server state' - pattern: 'Implement proper loading states'
Commit the Rules
.clinerules
in version controlTeam Collaboration
Rules Not Being Applied
Conflicting Rules
Performance Considerations
# Basic .clinerules Example project: name: 'Web Application' type: 'Next.js Frontend' standards: - 'Use TypeScript for all new code' - 'Follow React best practices' - 'Implement proper error handling' testing: unit: - 'Jest for unit tests' - 'React Testing Library for components' e2e: - 'Cypress for end-to-end testing' documentation: required: - 'README.md in each major directory' - 'JSDoc comments for public APIs' - 'Changelog updates for all changes'
# Advanced .clinerules Example project: name: 'Enterprise Application' compliance: - 'GDPR requirements' - 'WCAG 2.1 AA accessibility' architecture: patterns: - 'Clean Architecture principles' - 'Domain-Driven Design concepts' security: requirements: - 'OAuth 2.0 authentication' - 'Rate limiting on all APIs' - 'Input validation with Zod'
# TypeScript Performance Optimization Standards: Best Practices for Efficient Applications This document outlines coding standards and best practices specifically for performance optimization in TypeScript projects. Adhering to these guidelines will improve the speed, responsiveness, efficient use of resources, and overall user experience of your applications. ## Table of Contents - [1. Architectural Considerations for Performance](#1-architectural-considerations-for-performance) - [1.1. Code Splitting](#11-code-splitting) - [1.2. Lazy Loading Modules](#12-lazy-loading-modules) - [1.3. Server-Side Rendering (SSR) or Static Site Generation (SSG)](#13-server-side-rendering-ssr-or-static-site-generation-ssg) - [1.4. Data Structure Selection](#14-data-structure-selection) ## 1. Architectural Considerations for Performance ### 1.1. Code Splitting **Standard:** Implement code splitting to reduce the initial load time of your application. **Why:** Loading only the necessary code on initial page load significantly improves the user experience. **Do This:** * Utilize dynamic imports (`import()`) to load modules on demand. * Configure your bundler (Webpack, Parcel, Rollup) to create separate chunks for different parts of your application. **Don't Do This:** * Load the entire application code in a single bundle. * Use `require()` statements (CommonJS) in modern TypeScript projects where ES Modules are supported. **Example:** ```typescript // Before: Loading everything upfront import { featureA } from './featureA'; import { featureB } from './featureB'; // After: Code splitting with dynamic imports async function loadFeatureA() { const { featureA } = await import('./featureA'); featureA.init(); } async function loadFeatureB() { const { featureB } = await import('./featureB'); featureB.init(); } // Use loadFeatureA or loadFeatureB based on user interaction or route ``` **Bundler Configuration (Webpack example):** ```javascript // webpack.config.js module.exports = { entry: './src/index.ts', output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), }, module: { rules: [ { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/, }, ], }, resolve: { extensions: ['.tsx', '.ts', '.js'], }, optimization: { splitChunks: { chunks: 'all', // Split all chunks of code }, }, }; ``` ### 1.2. Lazy Loading Modules **Standard:** Employ lazy loading for non-critical modules or components. **Why:** Reduce the amount of code that needs to be parsed and compiled on initial load. **Do This:** * Load components or modules only when they are needed. * Utilize Intersection Observer API to load components when they become visible in the viewport. **Don't Do This:** * Load modules that are not immediately required for the current user interaction. **Example (Intersection Observer Lazy Loading):** ```typescript function lazyLoadComponent(element: HTMLElement, importPath: string) { const observer = new IntersectionObserver((entries) => { entries.forEach(async (entry) => { if (entry.isIntersecting) { const { default: Component } = await import(importPath); const componentInstance = new Component(); // Instantiate the component. element.appendChild(componentInstance.render()); // Append to the DOM (adjust according to your framework). observer.unobserve(element); } }); }); observer.observe(element); } // Usage: const lazyComponentElement = document.getElementById('lazy-component'); if (lazyComponentElement) { lazyLoadComponent(lazyComponentElement, './MyHeavyComponent'); } ``` ### 1.3. Server-Side Rendering (SSR) or Static Site Generation (SSG) **Standard:** Consider using SSR or SSG for content-heavy, SEO-sensitive, or performance-critical applications. **Why:** Reduces the time to first paint (TTFP) and improves SEO by providing crawlers with pre-rendered content. **Do This:** * Evaluate the trade-offs between SSR, SSG, and client-side rendering (CSR) based on your application's needs. * Use frameworks like Next.js (React), Nuxt.js (Vue), or Angular Universal. * Implement appropriate caching strategies for SSR. **Don't Do This:** * Default to CSR when SSR or SSG could provide significant performance benefits. **Example (Next.js):** ```typescript // pages/index.tsx (Next.js example) import React from 'react'; interface Props { data: { title: string; description: string; }; } const HomePage: React.FC<Props> = ({ data }) => { return ( <div> <h1>{data.title}</h1> <p>{data.description}</p> </div> ); }; export async function getServerSideProps() { // Fetch data from an API, database, or file system. const data = { title: 'My Awesome Website', description: 'Welcome to my extremely performant website!', }; return { props: { data, }, }; } export default HomePage; ``` ### 1.4. Data Structure Selection **Standard:** Select the most appropriate data structure for each specific use case. **Why:** Using appropriate data structures will reduce the complexity and improve the execution speed of algorithms. **Do This:** * Use `Map` when you need to associate keys with values, especially when the keys are not strings or numbers. * Use `Set` when you need to store a collection of unique values. * Use `Record<K, V>` type for type-safe object mapping. * Consider specialized data structures for specific performance needs (e.g., priority queues, linked lists). **Don't Do This:** * Use generic arrays or objects when more specialized data structures would be more efficient. * Perform frequent lookups in arrays when using a Map or Set would be more performant. **Example:** ```typescript // Before: Using an array for lookups const users = [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 3, name: 'Charlie' } ]; // O(n) lookup operation const findUser = (id: number) => users.find(user => user.id === id); // After: Using Map for efficient lookups const userMap = new Map<number, {id: number, name: string}>(); userMap.set(1, { id: 1, name: 'Alice' }); userMap.set(2, { id: 2, name: 'Bob' }); userMap.set(3, { id: 3, name: 'Charlie' }); // O(1) lookup operation const getUser = (id: number) => userMap.get(id); ```
Debe preferir usar el gestor de versiones "pnpm" por sobre "npm" para la ejecución de los comandos.
# Storybook Guidelines for Angular (v9.0.18+) Use these guidelines when working with Storybook in Angular projects. This document covers modern patterns, best practices, and the latest features in Storybook 9.x. ## 1. Core Architecture & Setup ### Framework Configuration - **Angular Framework**: Always use `@storybook/angular` as the framework in your `.storybook/main.ts` - **TypeScript First**: All configuration files should use TypeScript (`.ts` extension) - **Standalone Components**: Leverage Angular standalone components in stories - they work seamlessly with Storybook - **Modern Angular Patterns**: Support for Angular 17+ features including signals, control flow, and standalone APIs ### Basic Configuration Structure ```typescript // .storybook/main.ts import type { StorybookConfig } from '@storybook/angular'; const config: StorybookConfig = { framework: { name: '@storybook/angular', options: { // Framework-specific options }, }, stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], addons: [ '@storybook/addon-essentials', '@storybook/addon-interactions', '@storybook/addon-a11y', '@storybook/addon-docs', '@storybook/addon-vitest', // For Storybook 9.x testing ], }; export default config; ``` ## 2. Story Structure & CSF 3 Patterns ### Modern Story Format (CSF 3) - **Component Story Format 3**: Use the latest CSF 3 syntax for all new stories - **TypeScript Types**: Always use `Meta` and `StoryObj` for type safety - **Minimal Boilerplate**: Leverage CSF 3's simplified syntax ```typescript import type { Meta, StoryObj } from '@storybook/angular'; import { Button } from './button.component'; const meta: Meta = { component: Button, }; export default meta; type Story = StoryObj; export const Primary: Story = { args: { primary: true, label: 'Button', }, }; export const Secondary: Story = { args: { primary: false, label: 'Button', }, }; ``` ### Story Naming & Organization - **Descriptive Names**: Use clear, descriptive names for story exports - **Hierarchical Titles**: Organize stories with meaningful hierarchies using forward slashes - **Auto-Generated Titles**: Leverage automatic title generation when possible - **Component-Centric**: Group stories by component, not by state ```typescript const meta: Meta = { title: 'Design System/Components/Button', // Hierarchical organization component: Button, }; ``` ## 3. Angular-Specific Patterns ### Standalone Components (Recommended) - **Default Approach**: Prefer standalone components for new development - **No Module Imports**: Avoid importing CommonModule or other NgModules - **Direct Dependencies**: Import only required standalone components, directives, or pipes ```typescript // ✅ Good - Standalone component story import type { Meta, StoryObj } from '@storybook/angular'; import { MyStandaloneComponent } from './my-standalone.component'; const meta: Meta = { component: MyStandaloneComponent, }; ``` ### Legacy Module-Based Components - **Module Metadata**: Use `moduleMetadata` decorator for components requiring NgModules - **Minimal Imports**: Import only necessary modules and declarations ```typescript // For legacy components requiring modules import { moduleMetadata } from '@storybook/angular'; import { CommonModule } from '@angular/common'; const meta: Meta = { component: LegacyComponent, decorators: [ moduleMetadata({ imports: [CommonModule], declarations: [LegacyComponent, ChildComponent], }), ], }; ``` ### Application Configuration - **Provider Setup**: Use `applicationConfig` decorator for dependency injection - **Service Mocking**: Configure providers for testing scenarios ```typescript import { applicationConfig } from '@storybook/angular'; import { importProvidersFrom } from '@angular/core'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; const meta: Meta = { component: MyComponent, decorators: [ applicationConfig({ providers: [ importProvidersFrom(BrowserAnimationsModule), // Add other providers as needed ], }), ], }; ``` ## 4. Story Configuration & Best Practices ### Args and Controls - **Typed Args**: Leverage TypeScript for type-safe arguments - **Meaningful Defaults**: Provide sensible default values - **Control Types**: Explicitly define control types when needed ```typescript const meta: Meta = { component: Button, argTypes: { size: { control: { type: 'select' }, options: ['small', 'medium', 'large'], }, disabled: { control: { type: 'boolean' }, }, }, args: { // Default args for all stories size: 'medium', disabled: false, label: 'Button', }, }; ``` ### Parameters Configuration - **Global Parameters**: Set common parameters at the meta level - **Story-Specific Overrides**: Override parameters for specific stories when needed - **Documentation**: Use parameters for docs configuration ```typescript const meta: Meta = { component: Button, parameters: { docs: { description: { component: 'A versatile button component for user interactions.', }, }, backgrounds: { default: 'light', }, }, }; export const OnDark: Story = { parameters: { backgrounds: { default: 'dark' }, }, }; ``` ## 5. Advanced Patterns ### Custom Render Functions - **Complex Templates**: Use render functions for complex component templates - **Template Customization**: Provide custom templates when component alone isn't sufficient ```typescript export const WithCustomTemplate: Story = { render: args => ({ props: args, template: ` <p>Additional content around the button</p> `, }), }; ``` ### Component Composition - **Multi-Component Stories**: Show components working together - **Real-World Scenarios**: Create stories that demonstrate actual usage patterns ```typescript const meta: Meta = { component: List, decorators: [ moduleMetadata({ declarations: [List, ListItem], }), ], }; export const WithItems: Story = { render: args => ({ props: args, template: ` Item 1 Item 2 Item 3 `, }), }; ``` ## 6. Testing Integration (Storybook 9.x) ### Vitest Addon Integration - **Modern Testing**: Use `@storybook/addon-vitest` for component testing - **Story-Based Tests**: Transform stories into tests automatically - **Browser Mode**: Leverage Vitest's browser mode for realistic testing ```bash # Install and configure Vitest addon npx storybook@latest add @storybook/addon-vitest ``` ### Interaction Testing - **Play Functions**: Use play functions for interaction testing - **User Events**: Simulate real user interactions - **Assertions**: Include meaningful assertions in play functions ```typescript import { userEvent, within, expect } from '@storybook/test'; export const InteractiveTest: Story = { args: { label: 'Click me', }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); const button = canvas.getByRole('button'); await userEvent.click(button); await expect(button).toHaveClass('clicked'); }, }; ``` ### Accessibility Testing - **A11y Addon**: Include `@storybook/addon-a11y` for accessibility checks - **ARIA Labels**: Ensure proper ARIA labeling in stories - **Keyboard Navigation**: Test keyboard accessibility ```typescript const meta: Meta = { component: Button, parameters: { a11y: { config: { rules: [ { id: 'color-contrast', enabled: true, }, ], }, }, }, }; ``` ## 7. Documentation & Docs Integration ### Compodoc Integration - **API Documentation**: Integrate Compodoc for automatic API docs generation - **Angular.json Configuration**: Configure builders for Compodoc integration ```json // angular.json { "projects": { "your-project": { "architect": { "storybook": { "builder": "@storybook/angular:start-storybook", "options": { "compodoc": true, "compodocArgs": ["-e", "json", "-d", "."] } } } } } } ``` ```typescript // .storybook/preview.ts import { setCompodocJson } from '@storybook/addon-docs/angular'; import docJson from '../documentation.json'; setCompodocJson(docJson); ``` ### Story Documentation - **JSDoc Comments**: Use JSDoc comments for automatic documentation - **Description Parameters**: Override descriptions when needed - **Code Examples**: Include relevant code examples ```typescript /** * Primary button component for user actions. * Supports various sizes and states. */ const meta: Meta = { component: Button, parameters: { docs: { description: { component: 'The primary button component with full accessibility support.', }, }, }, }; /** * The primary button state - used for main actions */ export const Primary: Story = { parameters: { docs: { description: { story: 'Use this variant for primary actions like "Save" or "Submit".', }, }, }, }; ``` ## 8. Performance & Optimization ### Lazy Loading - **Code Splitting**: Leverage Angular's lazy loading for large component libraries - **Story Optimization**: Keep stories focused and lightweight - **Asset Management**: Optimize images and other assets ### Bundle Optimization - **Tree Shaking**: Ensure proper tree shaking of unused code - **Minimal Dependencies**: Import only necessary dependencies - **Build Configuration**: Optimize build configuration for production ## 9. Theming & Styling ### Angular Material Integration - **Theme Providers**: Use decorators to provide Angular Material themes - **Component Wrapper**: Wrap stories with theme providers when needed ```typescript import { componentWrapperDecorator } from '@storybook/angular'; const meta: Meta = { component: MyComponent, decorators: [componentWrapperDecorator(story => `${story}`)], }; ``` ### CSS Custom Properties - **Design Tokens**: Use CSS custom properties for consistent theming - **Theme Switching**: Implement theme switching in stories - **Responsive Design**: Test components across different viewports ## 10. File Organization & Naming ### File Structure - **Co-location**: Keep stories close to their components - **Consistent Naming**: Use consistent naming patterns - **Logical Grouping**: Group related stories together ``` src/ components/ button/ button.component.ts button.component.html button.component.scss button.component.stories.ts button.component.spec.ts ``` ### Naming Conventions - **Story Files**: Use `.stories.ts` extension - **Export Names**: Use PascalCase for story exports - **File Names**: Use kebab-case for file names ## 11. CI/CD Integration ### Build Configuration - **Static Builds**: Configure static build for deployment - **Environment Variables**: Handle environment-specific configuration - **Testing Pipeline**: Integrate story testing in CI/CD ```bash # Build Storybook for production ng run your-project:build-storybook # Run tests with Vitest addon npm run test-storybook ``` ### Deployment - **Static Hosting**: Deploy to static hosting services - **Version Management**: Tag releases with version numbers - **Documentation Updates**: Keep documentation in sync with code ## 12. Migration & Maintenance ### Upgrading Storybook - **Regular Updates**: Keep Storybook updated to latest versions - **Migration Guides**: Follow official migration guides - **Breaking Changes**: Test thoroughly after major updates ### Legacy Code Migration - **Gradual Migration**: Migrate stories incrementally - **CSF 2 to CSF 3**: Upgrade older story formats - **Module to Standalone**: Migrate to standalone components when possible ## 13. Common Patterns & Examples ### Form Components ```typescript export const FormExample: Story = { render: args => ({ props: args, template: ` `, }), }; ``` ### Data Loading States ```typescript export const Loading: Story = { args: { loading: true, data: null, }, }; export const WithData: Story = { args: { loading: false, data: mockData, }, }; export const Error: Story = { args: { loading: false, error: 'Failed to load data', }, }; ``` ### Responsive Components ```typescript export const Mobile: Story = { parameters: { viewport: { defaultViewport: 'mobile1', }, }, }; export const Desktop: Story = { parameters: { viewport: { defaultViewport: 'desktop', }, }, }; ``` ## 14. Quality Guidelines ### Code Quality - **TypeScript Strict Mode**: Use strict TypeScript configuration - **ESLint Rules**: Follow Storybook-specific ESLint rules - **Consistent Formatting**: Use Prettier for code formatting ### Testing Standards - **Coverage Goals**: Aim for high story coverage of component states - **Interaction Tests**: Include interaction tests for complex components - **Accessibility Tests**: Ensure all stories pass accessibility checks ### Documentation Standards - **Complete Coverage**: Document all component props and behaviors - **Real Examples**: Provide realistic usage examples - **Up-to-date**: Keep documentation synchronized with code changes ## 15. Troubleshooting & Common Issues ### Angular-Specific Issues - **Module Dependencies**: Ensure all required modules are imported - **Provider Configuration**: Check provider setup for dependency injection - **Change Detection**: Consider OnPush change detection strategy ### Performance Issues - **Bundle Size**: Monitor and optimize bundle size - **Memory Leaks**: Watch for memory leaks in complex stories - **Build Time**: Optimize build configuration for faster development This comprehensive guide ensures you're following the latest best practices for Storybook 9.x with Angular, leveraging modern features like the Vitest addon, improved testing capabilities, and optimized development workflows.
--- alwaysApply: true description: Avoid 'else'; prefer guard clauses and polymorphism to keep a linear flow --- ## Do not use the `else` keyword Avoid `else` to reduce branching and nesting. This keeps a linear top-to-bottom reading flow and simplifies methods. ### Preferred alternatives - **Guard clauses (early return/exit)**: handle exceptional cases up front and return immediately. - **Polymorphism**: for complex conditional logic based on type/state, use Strategy/State instead of chained conditionals. ### Example (guard clause) ❌ Bad: ```ts function processPayment(payment: Payment): boolean { if (payment.isVerified()) { console.log("Processing payment..."); return true; } else { console.log("Payment not verified."); return false; } } ``` ✅ Good: ```ts function processPayment(payment: Payment): boolean { // Guard clause if (!payment.isVerified()) { console.log("Payment not verified."); return false; } // Happy path console.log("Processing payment..."); return true; } ```
# Code Style and Conventions Standards for TypeScript This document outlines coding style and conventions standards for TypeScript development. Adhering to these standards promotes code consistency, readability, maintainability, and collaboration within development teams. These guidelines are tailored for the latest version of TypeScript and aim to leverage modern best practices. ## 1. Formatting Consistent formatting is crucial for readability. We adopt the following standards for TypeScript code formatting: ### 1.1. Whitespace and Indentation * **Standard:** Use 2 spaces for indentation. Avoid tabs. * **Why:** Consistent indentation enhances readability and reduces visual clutter. * **Do This:** """typescript function calculateArea(width: number, height: number): number { const area = width * height; return area; } """ * **Don't Do This:** """typescript function calculateArea(width: number, height: number): number { const area = width * height; return area; } """ * **Standard:** Use blank lines to separate logical sections of code within functions and classes. * **Why:** Separating logical blocks improves code comprehension. * **Do This:** """typescript function processData(data: any[]): void { // Validate data if (!data || data.length === 0) { throw new Error("Data is invalid."); } // Transform data const transformedData = data.map(item => ({ ...item, processed: true, })); // Save data saveToDatabase(transformedData); } """ ### 1.2. Line Length * **Standard:** Limit lines to a maximum of 120 characters. * **Why:** Enforces readability on various screen sizes and IDE configurations. * **How:** Configure your editor or IDE to display a line length guide at 120 characters. Break long lines at logical points, such as after commas, operators, or before opening parentheses. * **Do This:** """typescript const veryLongVariableName = calculateSomethingComplicated( param1, param2, param3 ); """ * **Don't Do This:** """typescript const veryLongVariableName = calculateSomethingComplicated(param1, param2, param3); """ ### 1.3. Braces and Parentheses * **Standard:** Use braces for all control flow statements, even single-line statements. * **Why:** Improves code clarity and reduces potential errors when modifying code. * **Do This:** """typescript if (isValid) { console.log("Valid"); } else { console.log("Invalid"); } """ * **Don't Do This:** """typescript if (isValid) console.log("Valid"); else console.log("Invalid"); """ * **Standard:** Use parentheses to clarify operator precedence, where needed. * **Why:** Reduces ambiguity, especially in complex expressions. * **Example:** """typescript const result = (a + b) * c; """ ### 1.4. Semicolons * **Standard:** Always use semicolons to terminate statements. * **Why:** Prevents unexpected behavior due to JavaScript's automatic semicolon insertion (ASI). * **Do This:** """typescript const name = "John"; console.log(name); """ * **Don't Do This:** """typescript const name = "John" console.log(name) """ ## 2. Naming Conventions Consistent naming conventions are essential for code clarity and maintainability. ### 2.1. Variables and Constants * **Standard:** Use camelCase for variable and constant names. * **Why:** Widely adopted convention for JavaScript and TypeScript. * **Do This:** """typescript const userName = "Alice"; let itemCount = 0; """ * **Standard:** Use UPPER_SNAKE_CASE for constant values (i.e. values that may be inlined for performance or are known at compile time). * **Why:** Clearly distinguishes constants from variables. * **Do This:** """typescript const MAX_RETRIES = 3; const API_ENDPOINT = "https://example.com/api"; """ ### 2.2. Functions and Methods * **Standard:** Use camelCase for function and method names. * **Why:** Follows common JavaScript/TypeScript conventions. * **Do This:** """typescript function calculateTotal(price: number, quantity: number): number { return price * quantity; } class ShoppingCart { addItem(item: string): void { console.log("Adding ${item} to the cart."); } } """ ### 2.3. Classes and Interfaces * **Standard:** Use PascalCase for class and interface names. * **Why:** Clearly identifies classes and interfaces. * **Do This:** """typescript interface User { id: number; name: string; } class Product { constructor(public name: string, public price: number) {} } """ ### 2.4. Type Parameters * **Standard:** Use single uppercase letters, typically "T", "U", "V", etc., for generic type parameters. * **Why:** Follows established TypeScript conventions. * **Do This:** """typescript function identity<T>(arg: T): T { return arg; } """ ### 2.5. Boolean Variables * **Standard:** Prefix boolean variables with "is", "has", or "should" to indicate a boolean value. * **Why:** Improves readability by clearly indicating the purpose of the variable. * **Do This:** """typescript let isValid: boolean = true; let hasPermission: boolean = false; let shouldUpdate: boolean = true; """ ## 3. Stylistic Consistency Consistency in style is critical for code maintainability. ### 3.1. Type Annotations and Inference * **Standard:** Use explicit type annotations where type inference is not obvious, especially for function parameters and return types. * **Why:** Improves code clarity and helps catch type-related errors early. * **Do This:** """typescript function greet(name: string): string { return "Hello, ${name}!"; } const add: (x: number, y: number) => number = (x, y) => x + y; """ * **Don't Do This:** """typescript function greet(name) { // Implicit 'any' type return "Hello, ${name}!"; } """ * **Standard:** Leverage type inference for local variables when the type is immediately apparent. * **Why:** Reduces verbosity and keeps code concise. * **Do This:** """typescript const message = "Hello, world!"; // Type inferred as string const count = 10; // Type inferred as number """ * **Standard:** When initializing variables with "null" or "undefined", explicitly define the type. * **Why:** Helps avoid unexpected type-related issues later. * **Do This:** """typescript let user: User | null = null; let data: string[] | undefined = undefined; """ ### 3.2. String Usage * **Standard:** Prefer template literals for string concatenation and multi-line strings. * **Why:** More readable and easier to maintain compared to traditional string concatenation. * **Do This:** """typescript const name = "Alice"; const message = "Hello, ${name}!"; const multiLine = "This is a multi-line string."; """ * **Don't Do This:** """typescript const name = "Alice"; const message = "Hello, " + name + "!"; const multiLine = "This is a\n" + "multi-line string."; """ ### 3.3. Object Literals * **Standard:** Use shorthand notation for object properties when the property name matches the variable name. * **Why:** Improves code conciseness and readability. * **Do This:** """typescript const name = "Alice"; const age = 30; const user = { name, age }; // Shorthand notation """ * **Don't Do This:** """typescript const name = "Alice"; const age = 30; const user = { name: name, age: age }; """ * **Standard:** Use object spread syntax for creating copies of objects or merging objects. * **Why:** More concise and readable than older methods like "Object.assign()". * **Do This:** """typescript const original = { a: 1, b: 2 }; const copy = { ...original, c: 3 }; // Creates a new object with a, b, and c """ ### 3.4. Arrow Functions * **Standard:** Use arrow functions for concise function expressions, especially for callbacks and inline functions. * **Why:** More compact syntax and lexically binds "this". * **Do This:** """typescript const numbers = [1, 2, 3]; const squared = numbers.map(x => x * x); // Concise arrow function """ * **Don't Do This:** """typescript const numbers = [1, 2, 3]; const squared = numbers.map(function(x) { return x * x; }); """ * **Standard:** Omit parentheses for single-parameter arrow functions. * **Why:** Makes the code even more concise. * **Do This:** """typescript const increment = x => x + 1; """ * **Don't Do This:** """typescript const increment = (x) => x + 1; """ ### 3.5. Modern TypeScript Features * **Standard:** Leverage features like optional chaining ("?.") and nullish coalescing ("??") for safer and more concise code. Optional properties on interfaces may be relevant if these are in use. * **Why:** Reduces boilerplate and improves null/undefined handling. * **Do This:** """typescript interface User { profile?: { address?: { city?: string; } } } const user: User = {}; const city = user?.profile?.address?.city ?? "Unknown"; // Nullish coalescing console.log(city); interface Config { timeout?: number; } const defaultConfig: Config = { timeout: 5000, }; function initialize(config?: Config) { const timeout = config?.timeout ?? defaultConfig.timeout; console.log("Timeout: ${timeout}"); } initialize(); // Timeout: 5000 initialize({ timeout: 10000 }); // Timeout: 10000 """ * **Standard:** Utilize discriminated unions and exhaustive checks for increased type safety and maintainability. * **Why:** Improves type correctness and makes it easier to handle different states or object types. * **Do This:** """typescript interface Success { type: "success"; result: any; } interface Error { type: "error"; message: string; } type Result = Success | Error; function handleResult(result: Result) { switch (result.type) { case "success": console.log("Success:", result.result); break; case "error": console.error("Error:", result.message); break; default: // Exhaustive check: TypeScript will flag this if a new type is added to Result const _exhaustiveCheck: never = result; return _exhaustiveCheck; } } const successResult: Success = { type: "success", result: { data: "example" } }; const errorResult: Error = { type: "error", message: "Something went wrong" }; handleResult(successResult); handleResult(errorResult); """ ### 3.6. Asynchronous Code * **Standard:** Always use "async/await" syntax for asynchronous operations. * **Why:** Improves readability and simplifies error handling compared to traditional promise chains. * **Do This:** """typescript async function fetchData(): Promise<any> { try { const response = await fetch("https://example.com/api/data"); const data = await response.json(); return data; } catch (error) { console.error("Error fetching data:", error); throw error; } } """ * **Don't Do This:** """typescript function fetchData(): Promise<any> { return fetch("https://example.com/api/data") .then(response => response.json()) .then(data => data) .catch(error => { console.error("Error fetching data:", error); throw error; }); } """ * **Standard:** Use "Promise.all" for concurrent asynchronous operations that don't depend on each other. * **Why:** Improves performance by executing asynchronous tasks in parallel. * **Do This:** """typescript async function processData(): Promise<void> { const [result1, result2] = await Promise.all([ fetchData1(), fetchData2(), ]); console.log("Result 1:", result1); console.log("Result 2:", result2); } """ ### 3.7 Error Handling * **Standard:** Implement robust error handling using "try...catch" blocks, especially in asynchronous functions. * **Why:** Prevents unhandled exceptions and allows for graceful recovery. * **Do This:** """typescript async function doSomething() { try { const result = await someAsyncOperation(); console.log("Result:", result); } catch (error) { console.error("An error occurred:", error); // Implement specific error handling/logging } } """ * **Standard:** Create custom error classes to provide more context and specify error handling logic. * **Why:** Extends the built-in Error to communicate more specific information about the error to the outside world. * **Do This:** """typescript class CustomError extends Error { constructor(message: string, public errorCode: number) { super(message); this.name = "CustomError"; Object.setPrototypeOf(this, CustomError.prototype); } } async function performOperation() { try { // some operation throw new CustomError("Operation failed", 500); } catch (error) { if (error instanceof CustomError) { console.error("Custom Error ${error.errorCode}: ${error.message}"); } else { console.error("An unexpected error occurred:", error); } } } """ ### 3.8 Immutability * **Standard:** Strive for immutability whenever practical, using "const" for variables that should not be reassigned and avoiding direct modification of objects and arrays. * **Why:** Makes code more predictable and easier to reason about, reducing the risk of bugs. * **Do This:** """typescript const originalArray = [1, 2, 3]; const newArray = [...originalArray, 4]; // Creates a new array const originalObject = { a: 1, b: 2 }; const newObject = { ...originalObject, c: 3 }; // Creates a new object """ * **Don't Do This:** """typescript const originalArray = [1, 2, 3]; originalArray.push(4); // Modifies the original array const originalObject = { a: 1, b: 2 }; originalObject.c = 3; // Modifies the original object """ ### 3.9 Comments * **Standard:** Add comments to explain complex or non-obvious logic, but prioritize writing self-documenting code. * **Why:** Comments should supplement, not replace, clear code. * **Guidelines:** * Use JSDoc-style comments for documenting functions, classes, and interfaces. * Explain the *why*, not the *what*. The code should explain what it does. * Keep comments concise and up-to-date. * Remove outdated or redundant comments. * **Example:** """typescript /** * Calculates the area of a rectangle. * @param width The width of the rectangle. * @param height The height of the rectangle. * @returns The area of the rectangle. */ function calculateArea(width: number, height: number): number { return width * height; } """ ## 4. Technology Specific Details (Distinguishing Good from Great) * **Standard:** Take advantage of TypeScript's advanced type features to create robust and maintainable code structures. * **Guidelines:** * **Utility Types:** Employ TypeScript's utility types ("Partial", "Readonly", "Pick", "Omit", "Record", etc.) to manipulate types and generate new types efficiently. """typescript interface User { id: number; name: string; email: string; age: number; } // Make all properties optional type PartialUser = Partial<User>; // Make specified properties required type RequiredIdAndName = Required<Pick<User, 'id' | 'name'>>; // Type with only certain properties type UserInfo = Pick<User, 'name' | 'email'>; // Type without certain properties type UserWithoutId = Omit<User, 'id'>; """ * **Mapped Types:** Utilize mapped types to transform the properties of an existing type, providing a dynamic way to define new types based on existing ones. """typescript interface Product { id: string; name: string; price: number; } // Create a read-only version of Product type ReadonlyProduct = Readonly<Product>; // Create a type where all properties of Product are nullable type NullableProduct = { [K in keyof Product]: Product[K] | null; }; """ * **Conditional Types:** Conditional types allow to define types based on conditions, adding yet another powerful layer of abstraction to type definitions. They help to ensure type safety throughout an application. """typescript type NonNullableProperty<T, K extends keyof T> = T[K] extends null | undefined ? never : K; type RequiredProperties<T> = Pick<T, NonNullableProperty<T, keyof T>>; interface Configuration { host?: string; port?: number; // Port can be potentially undefined or null timeout?: number; } // Extracts properties that are guaranteed to be assigned during runtime. type RuntimeProperties = RequiredProperties<Configuration>; // Result equivalent to {timeout: number;} """ * **Decorators:** Use decorators to add metadata or modify the behavior of classes, methods, properties, or parameters. * **Why:** Provide a declarative and reusable way to add functionality, such as logging, validation, or dependency injection. * **Example:** """typescript function logMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; descriptor.value = function(...args: any[]) { console.log("Calling method ${propertyKey} with arguments: ${JSON.stringify(args)}"); const result = originalMethod.apply(this, args); console.log("Method ${propertyKey} returned: ${result}"); return result; }; return descriptor; } class Calculator { @logMethod add(a: number, b: number): number { return a + b; } } const calculator = new Calculator(); calculator.add(2, 3); // Output: // Calling method add with arguments: [2,3] // Method add returned: 5 """ ## 5. Tooling * **Standard:** Use Prettier for automatic code formatting and ESLint with recommended TypeScript rules for linting. * **Why:** Automates formatting and enforces code quality, reducing manual effort and improving consistency. * **Configuration:** Configure Prettier and ESLint to work seamlessly with your IDE and CI/CD pipeline. * **Example ".eslintrc.js" configuration:** """javascript module.exports = { parser: "@typescript-eslint/parser", parserOptions: { ecmaVersion: 2020, sourceType: "module", project: './tsconfig.json', }, plugins: ["@typescript-eslint"], extends: [ "eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-requiring-type-checking", "prettier", ], rules: { // Add or override rules here }, }; """ By adhering to these coding standards, TypeScript projects will benefit from improved code quality, readability, and maintainability, fostering a collaborative and productive development environment.