259 lines
6.2 KiB
Markdown
259 lines
6.2 KiB
Markdown
# Code Style Guidelines
|
|
|
|
## Core Code Style Principles
|
|
Twenty emphasizes clean, readable, and maintainable code. This document outlines our code style conventions and best practices.
|
|
|
|
## Control Flow
|
|
|
|
### Early Returns
|
|
- Use early returns to reduce nesting
|
|
- Handle edge cases first
|
|
```typescript
|
|
// ✅ Correct
|
|
const processUser = (user: User | null) => {
|
|
if (!user) return null;
|
|
if (!user.isActive) return null;
|
|
|
|
return {
|
|
id: user.id,
|
|
name: user.name,
|
|
};
|
|
};
|
|
|
|
// ❌ Incorrect
|
|
const processUser = (user: User | null) => {
|
|
if (user) {
|
|
if (user.isActive) {
|
|
return {
|
|
id: user.id,
|
|
name: user.name,
|
|
};
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
```
|
|
|
|
### No Nested Ternaries
|
|
- Avoid nested ternary operators
|
|
- Use if statements or early returns
|
|
```typescript
|
|
// ✅ Correct
|
|
const getUserDisplay = (user: User) => {
|
|
if (!user.name) return 'Anonymous';
|
|
if (!user.isActive) return 'Inactive User';
|
|
return user.name;
|
|
};
|
|
|
|
// ❌ Incorrect
|
|
const getUserDisplay = (user: User) =>
|
|
user.name
|
|
? user.isActive
|
|
? user.name
|
|
: 'Inactive User'
|
|
: 'Anonymous';
|
|
```
|
|
|
|
### No Else-If Chains
|
|
- Use switch statements or lookup objects
|
|
- Keep conditions flat
|
|
```typescript
|
|
// ✅ Correct
|
|
const getStatusColor = (status: Status): string => {
|
|
switch (status) {
|
|
case 'success':
|
|
return 'green';
|
|
case 'warning':
|
|
return 'yellow';
|
|
case 'error':
|
|
return 'red';
|
|
default:
|
|
return 'gray';
|
|
}
|
|
};
|
|
|
|
// Or using a lookup object
|
|
const statusColors: Record<Status, string> = {
|
|
success: 'green',
|
|
warning: 'yellow',
|
|
error: 'red',
|
|
default: 'gray',
|
|
};
|
|
|
|
// ❌ Incorrect
|
|
const getStatusColor = (status: Status): string => {
|
|
if (status === 'success') {
|
|
return 'green';
|
|
} else if (status === 'warning') {
|
|
return 'yellow';
|
|
} else if (status === 'error') {
|
|
return 'red';
|
|
} else {
|
|
return 'gray';
|
|
}
|
|
};
|
|
```
|
|
|
|
## Operators and Expressions
|
|
|
|
### Optional Chaining Over &&
|
|
- Use optional chaining for null/undefined checks
|
|
- Clearer intent and better type safety
|
|
```typescript
|
|
// ✅ Correct
|
|
const userName = user?.name;
|
|
const userAddress = user?.address?.street;
|
|
|
|
// ❌ Incorrect
|
|
const userName = user && user.name;
|
|
const userAddress = user && user.address && user.address.street;
|
|
```
|
|
|
|
## Function Design
|
|
|
|
### Small Focused Functions
|
|
- Keep functions small and single-purpose
|
|
- Extract complex logic into helper functions
|
|
```typescript
|
|
// ✅ Correct
|
|
const validateUser = (user: User) => {
|
|
if (!isValidName(user.name)) return false;
|
|
if (!isValidEmail(user.email)) return false;
|
|
if (!isValidAge(user.age)) return false;
|
|
return true;
|
|
};
|
|
|
|
const isValidName = (name: string) => {
|
|
return name.length >= 2 && /^[a-zA-Z\s]*$/.test(name);
|
|
};
|
|
|
|
const isValidEmail = (email: string) => {
|
|
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
};
|
|
|
|
const isValidAge = (age: number) => {
|
|
return age >= 18 && age <= 120;
|
|
};
|
|
|
|
// ❌ Incorrect
|
|
const validateUser = (user: User) => {
|
|
if (user.name.length < 2 || !/^[a-zA-Z\s]*$/.test(user.name)) return false;
|
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(user.email)) return false;
|
|
if (user.age < 18 || user.age > 120) return false;
|
|
return true;
|
|
};
|
|
```
|
|
|
|
## Naming and Documentation
|
|
|
|
### Clear Variable Names
|
|
- Use descriptive, intention-revealing names
|
|
- Avoid abbreviations unless common
|
|
```typescript
|
|
// ✅ Correct
|
|
const isUserActive = user.status === 'active';
|
|
const hasRequiredPermissions = user.permissions.includes('admin');
|
|
const userDisplayName = user.name || 'Anonymous';
|
|
|
|
// ❌ Incorrect
|
|
const active = user.status === 'active';
|
|
const hasPerm = user.permissions.includes('admin');
|
|
const udn = user.name || 'Anonymous';
|
|
```
|
|
|
|
### No Console.logs in Commits
|
|
- Remove all console.logs before committing
|
|
- Use proper logging/error tracking in production
|
|
```typescript
|
|
// ❌ Incorrect - Don't commit these
|
|
console.log('user:', user);
|
|
console.log('debug:', someValue);
|
|
|
|
// ✅ Correct - Use proper logging
|
|
logger.info('User action completed', { userId: user.id });
|
|
logger.error('Operation failed', { error });
|
|
```
|
|
|
|
### Minimal Comments
|
|
- Write self-documenting code
|
|
- Use comments only for complex business logic
|
|
```typescript
|
|
// ✅ Correct
|
|
// Calculate pro-rated amount based on billing cycle
|
|
const calculateProRatedAmount = (amount: number, daysLeft: number, totalDays: number) => {
|
|
return (amount * daysLeft) / totalDays;
|
|
};
|
|
|
|
// ❌ Incorrect - Unnecessary comments
|
|
// Get the user's name
|
|
const getUserName = (user: User) => user.name;
|
|
|
|
// Check if user is active
|
|
const isUserActive = (user: User) => user.status === 'active';
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Proper Error Handling
|
|
- Use try-catch blocks appropriately
|
|
- Provide meaningful error messages
|
|
```typescript
|
|
// ✅ Correct
|
|
const fetchUserData = async (userId: string) => {
|
|
try {
|
|
const response = await api.get(`/users/${userId}`);
|
|
return response.data;
|
|
} catch (error) {
|
|
logger.error('Failed to fetch user data', {
|
|
userId,
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
});
|
|
throw new UserFetchError('Failed to fetch user data');
|
|
}
|
|
};
|
|
|
|
// ❌ Incorrect
|
|
const fetchUserData = async (userId: string) => {
|
|
try {
|
|
const response = await api.get(`/users/${userId}`);
|
|
return response.data;
|
|
} catch (error) {
|
|
console.log('error:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
```
|
|
|
|
## Code Organization
|
|
|
|
### Logical Grouping
|
|
- Group related code together
|
|
- Maintain consistent organization
|
|
```typescript
|
|
// ✅ Correct
|
|
class UserService {
|
|
// Properties
|
|
private readonly api: Api;
|
|
private readonly logger: Logger;
|
|
|
|
// Constructor
|
|
constructor(api: Api, logger: Logger) {
|
|
this.api = api;
|
|
this.logger = logger;
|
|
}
|
|
|
|
// Public methods
|
|
public async getUser(id: string): Promise<User> {
|
|
// Implementation
|
|
}
|
|
|
|
public async updateUser(user: User): Promise<User> {
|
|
// Implementation
|
|
}
|
|
|
|
// Private helpers
|
|
private validateUser(user: User): boolean {
|
|
// Implementation
|
|
}
|
|
}
|
|
``` |