Opinionated frontend development standards for modern React + TypeScript applications. Covers Suspense-first data fetching, lazy loading, feature-based architecture, MUI v7 styling, TanStack Router, performance optimization, and strict TypeScript practices.
67
67%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
TypeScript best practices for type safety and maintainability in React frontend code.
TypeScript strict mode is enabled in the project:
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}This means:
any typesany Type// ❌ NEVER use any
function handleData(data: any) {
return data.something;
}
// ✅ Use specific types
interface MyData {
something: string;
}
function handleData(data: MyData) {
return data.something;
}
// ✅ Or use unknown for truly unknown data
function handleUnknown(data: unknown) {
if (typeof data === 'object' && data !== null && 'something' in data) {
return (data as MyData).something;
}
}If you truly don't know the type:
unknown (forces type checking)// ✅ CORRECT - Explicit return type
function getUser(id: number): Promise<User> {
return apiClient.get(`/users/${id}`);
}
function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// ❌ AVOID - Implicit return type (less clear)
function getUser(id: number) {
return apiClient.get(`/users/${id}`);
}// React.FC already provides return type (ReactElement)
export const MyComponent: React.FC<Props> = ({ prop }) => {
return <div>{prop}</div>;
};
// For custom hooks
function useMyData(id: number): { data: Data; isLoading: boolean } {
const [data, setData] = useState<Data | null>(null);
const [isLoading, setIsLoading] = useState(true);
return { data: data!, isLoading };
}// ✅ CORRECT - Explicitly mark as type import
import type { User } from '~types/user';
import type { Post } from '~types/post';
import type { SxProps, Theme } from '@mui/material';
// ❌ AVOID - Mixed value and type imports
import { User } from '~types/user'; // Unclear if type or valueBenefits:
/**
* Props for MyComponent
*/
interface MyComponentProps {
/** The user ID to display */
userId: number;
/** Optional callback when action completes */
onComplete?: () => void;
/** Display mode for the component */
mode?: 'view' | 'edit';
/** Additional CSS classes */
className?: string;
}
export const MyComponent: React.FC<MyComponentProps> = ({
userId,
onComplete,
mode = 'view', // Default value
className,
}) => {
return <div>...</div>;
};Key Points:
?interface ContainerProps {
children: React.ReactNode;
title: string;
}
// React.FC automatically includes children type, but be explicit
export const Container: React.FC<ContainerProps> = ({ children, title }) => {
return (
<div>
<h2>{title}</h2>
{children}
</div>
);
};// Make all properties optional
type UserUpdate = Partial<User>;
function updateUser(id: number, updates: Partial<User>) {
// updates can have any subset of User properties
}// Select specific properties
type UserPreview = Pick<User, 'id' | 'name' | 'email'>;
const preview: UserPreview = {
id: 1,
name: 'John',
email: 'john@example.com',
// Other User properties not allowed
};// Exclude specific properties
type UserWithoutPassword = Omit<User, 'password' | 'passwordHash'>;
const publicUser: UserWithoutPassword = {
id: 1,
name: 'John',
email: 'john@example.com',
// password and passwordHash not allowed
};// Make all properties required
type RequiredConfig = Required<Config>; // All optional props become required// Type-safe object/map
const userMap: Record<string, User> = {
'user1': { id: 1, name: 'John' },
'user2': { id: 2, name: 'Jane' },
};
// For styles
import type { SxProps, Theme } from '@mui/material';
const styles: Record<string, SxProps<Theme>> = {
container: { p: 2 },
header: { mb: 1 },
};function isUser(data: unknown): data is User {
return (
typeof data === 'object' &&
data !== null &&
'id' in data &&
'name' in data
);
}
// Usage
if (isUser(response)) {
console.log(response.name); // TypeScript knows it's User
}type LoadingState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: Data }
| { status: 'error'; error: Error };
function Component({ state }: { state: LoadingState }) {
// TypeScript narrows type based on status
if (state.status === 'success') {
return <Display data={state.data} />; // data available here
}
if (state.status === 'error') {
return <Error error={state.error} />; // error available here
}
return <Loading />;
}function getById<T>(items: T[], id: number): T | undefined {
return items.find(item => (item as any).id === id);
}
// Usage with type inference
const users: User[] = [...];
const user = getById(users, 123); // Type: User | undefinedinterface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
export function List<T>({ items, renderItem }: ListProps<T>): React.ReactElement {
return (
<div>
{items.map((item, index) => (
<div key={index}>{renderItem(item)}</div>
))}
</div>
);
}
// Usage
<List<User>
items={users}
renderItem={(user) => <UserCard user={user} />}
/>// ✅ OK - When you know more than TypeScript
const element = document.getElementById('my-element') as HTMLInputElement;
const value = element.value;
// ✅ OK - API response that you've validated
const response = await api.getData();
const user = response.data as User; // You know the shape// ❌ AVOID - Circumventing type safety
const data = getData() as any; // WRONG - defeats TypeScript
// ❌ AVOID - Unsafe assertion
const value = unknownValue as string; // Might not actually be string// ✅ CORRECT
const name = user?.profile?.name;
// Equivalent to:
const name = user && user.profile && user.profile.name;// ✅ CORRECT
const displayName = user?.name ?? 'Anonymous';
// Only uses default if null or undefined
// (Different from || which triggers on '', 0, false)// ✅ OK - When you're certain value exists
const data = queryClient.getQueryData<Data>(['data'])!;
// ⚠️ CAREFUL - Only use when you KNOW it's not null
// Better to check explicitly:
const data = queryClient.getQueryData<Data>(['data']);
if (data) {
// Use data
}TypeScript Checklist:
any type (use unknown if needed)import type for type importsSee Also: