Optimizing Team Productivity: Key Front-End Coding Practices with TypeScript and React
Tags: frontend, beginners, typescript, react
Published: 2024-03-18 13:01:36

With the recent expansion of our development team, we found it crucial to establish a front-end coding convention aimed at preserving readability and maintainability. This article focuses primarily on practices within TypeScript and React. We hope this serves as a valuable resource for you.

## Naming Conventions for Functions and Variables

### CONSTANT_CASE

Global constants are named in all uppercase letters, with words separated by underscores.

```typescript
const MAX_USERS = 5;
```

### UpperCamelCase

Classes, types, and type parameters follow the UpperCamelCase convention, starting with a capital letter.

```typescript
class UserAccount {}
type UserType = { name: string; age: number; };
```

### lowerCamelCase

Variables, parameters, functions, methods, properties, and module aliases adhere to the lowerCamelCase convention, starting with a lowercase letter.

```typescript
let userName: string = "John Doe";
function getUserInfo(userId: number) {...}
```

### Handling Proper Nouns

Proper nouns also follow the naming conventions, adapting to camelCase as appropriate.

- `URLPath` → `UrlPath`, `urlPath`
- `iOSDevice` → `IosDevice`, `iosDevice`

### Usage of Multiple Words

Names other than root `App` or basic components are constructed using multiple words to ensure clarity.

```jsx
// Good
const TodoList = () => <div>...</div>;
type ShopCategory = "Grocery Store" | "Cafe" | "Italian Restaurant";
```

### Initials and Abbreviations

Project-specific abbreviations that might not be immediately clear to new team members should be avoided.

```typescript
// Good
let backendManager = new BackendManager();
```

## Comments

### Avoid Stating the Obvious

Omit comments when the name itself makes the intent clear.

### Explain the "Why," Not the "What"

Comments should clarify why a piece of code is written, not what it does.

```typescript
// WHY: To ensure the user is of legal age
if (user.age >= 18) {...}
```

### Marking Code Imperfections

Use `TODO`, `NOTE`, `FIXME` to highlight areas needing improvement or attention.

```typescript
// TODO: Improve performance
// FIXME: Incomplete error handling
```

## TypeScript-Specific Guidelines

### Favor named export Over default export

Named exports enhance code clarity and reusability.

```typescript
// Good
export const getUser = () => {...};
```

### Prefer type Over interface

`type` is favored for its flexibility and the ease with which its contents can be inspected in VS Code.

```typescript
type User = {
  name: string;
  age: number;
};
```

### Use Union Types Instead of enum

Union Types are preferred for their direct impact on the compiled code, utilizing TypeScript's type advantages without runtime overhead.

```typescript
type Role = "admin" | "user" | "guest";
```

### Prioritize undefined Over null

Using `undefined` over `null` simplifies the TypeScript type system and is generally recommended.

```typescript
let user: User | undefined = undefined;
```

## Components

### Append Basic Component Names Last

Names end with basic component descriptors (Container, View, Button, etc.) to clarify their roles.

```jsx
const UserProfileContainer = () => <div>...</div>;
```

### Order of Words

Start with the most general word and end with descriptive modifiers for clear naming.

```jsx
// Good
const SearchResultList = () => <div>...</div>;
```

### Tightly Coupled Components

Names of components that are closely related should reflect their association, often by prefixing with the parent component's name.

```jsx
const TodoList = () => <ul>...</ul>;
const TodoListItem = () => <li>...</li>;
```

### Typing Props

Clearly define expected properties through type annotations to maintain component contract integrity.

```tsx
type Props = {
  onClick: () => void;
  title: string;
};
const Button: React.FC<Props> = ({ onClick, title }) => (
  <button onClick={onClick}>{title}</button>
);
```

### Avoid the Spread Syntax in Props

To prevent unnecessary properties from being passed, use the spread syntax cautiously.

```jsx
// Good
const userDetails = { name: "John", age: 30 };
<UserProfile {...userDetails} />
```

### Writing Conditional Statements

**Always use {} to wrap if statements**

Even when the statement can be written in one line, wrapping it in braces enhances readability.

```jsx
// Not recommended
if (condition) return;

// Recommended
if (condition) {
  return;
}
```

### Comparing Boolean Values

**Prefer comparing directly to true**

Explicit comparison makes the intention of your code clearer.

```jsx
// Not recommended
if (!disabled) {
  doSomething();
}

// Recommended
if (enabled) {
  doSomething();
}
```

For boolean variables, use prefixes like `is`, `has`, `can`, `should` to clarify their type. However, it's common practice not to apply this rule to component props.

```jsx
// Not recommended
<RoundButton isDisabled={true} />

// Recommended
<RoundButton disabled />
```

### Using Ternary Operators

**Avoid ternary operators when not using the return value**

```jsx
// Not recommended
condition ? doSomething() : doSomethingElse();

// Recommended
if (condition) {
  doSomething();
} else {
  doSomethingElse();
}
```

### Choosing Between Switch Statements and Map Objects

**Consider Map objects for managing key-value based logic**

Maps can be particularly useful for handling cases based on key-value pairs.

```jsx
// Example using switch statement
switch (type) {
  case "walk":
  case "bicycle":
    return "Back Load";
  case "motorcycle":
  case "car":
    return "Main Load";
}

// Example using a Map object
const vehicleLoadMap = {
  walk: "Back Load",
  bicycle: "Back Load",
  motorcycle: "Main Load",
  car: "Main Load"
};

return vehicleLoadMap[type];
```

### Defining Event Handlers

**Define event handlers outside of component body**

This practice keeps the component body clean and improves readability.

### Strategies for Memoization

**Apply memoization judiciously**

- Consider wrapping frequently re-rendered or seldom-changing props components with `React.memo`.
- Memoization can enhance performance but may introduce overhead if misused, so choose your scenarios wisely.

### Utilizing String Literal Types

**Prefer string literal types over enums for type safety without redundancy**

This approach ensures type safety while avoiding unnecessary code.

```typescript
// Not recommended
const ALERT_TYPE = {
  INFO: "INFO",
  SUCCESS: "SUCCESS",
  WARNING: "WARNING",
} as const;

// Recommended
type AlertType = "INFO" | "SUCCESS" | "WARNING";
```

### Applying CSS

**Use `clsx` to manage class names based on conditions**

```tsx
// Example using `clsx`
import clsx from 'clsx';

<div className={clsx([styles.someStyle, isFoo && styles.fooStyle])}>
  Text here
</div>
```

### Specifying Values for Props

**Follow a unified naming convention for passing props**

This ensures the purpose of props is clear, facilitating data passing between components.

```tsx
// Naming example for passing a collection
const TodoList: React.FC<{ items: TodoItem[] }> = ({ items }) => {...};

// Naming example for passing a single object
const TodoItemDetail: React.FC<{ item: TodoItem }> = ({ item }) => {...};
```

## Final Thoughts

This guideline aims to uphold consistency and improve efficiency in front-end development using TypeScript and React. Adjustments may be necessary depending on project or team specifics, but striving for best practices is paramount. Through these conventions, we hope to enhance the readability and maintainability of our projects, facilitating smoother development processes and collaborations within the team.