DEV Community

Cover image for Best Practices for Using TypeScript in React with Vite
obei
obei

Posted on

Best Practices for Using TypeScript in React with Vite

Best Practices for Using TypeScript in React with Vite

Vite is a modern, fast build tool that provides an excellent development experience for modern web projects. Combining Vite with TypeScript and React allows developers to harness the power of fast builds, hot module replacement, and a highly efficient development workflow. This article explores the best practices for using TypeScript in React projects with Vite.

1. Setting Up TypeScript with React in Vite

To get started with TypeScript in a React project using Vite, you can create a new project with the following commands:

npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
Enter fullscreen mode Exit fullscreen mode

This command sets up a new React project with TypeScript using Vite's template.

2. Strict Type-Checking Options

Enable strict type-checking options in your tsconfig.json to ensure a higher level of type safety:

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "alwaysStrict": true
  }
}
Enter fullscreen mode Exit fullscreen mode

These settings help catch potential bugs early and enforce best practices in type handling.

3. Use Functional Components and Hooks

Prefer using functional components and React hooks over class components. Functional components are simpler and more concise, and hooks work seamlessly with TypeScript. Here’s an example of a typed functional component using hooks:

import React, { useState, FC } from 'react';

interface CounterProps {
  initialCount: number;
}

const Counter: FC<CounterProps> = ({ initialCount }) => {
  const [count, setCount] = useState<number>(initialCount);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default Counter;
Enter fullscreen mode Exit fullscreen mode

4. Typing Props and State

Define clear types for props and state. Use interfaces for props to benefit from TypeScript’s structural typing:

interface ButtonProps {
  label: string;
  onClick: () => void;
}

const Button: FC<ButtonProps> = ({ label, onClick }) => (
  <button onClick={onClick}>{label}</button>
);
Enter fullscreen mode Exit fullscreen mode

5. Use Type Inference and Avoid any

TypeScript's type inference is powerful, so you often don't need to explicitly annotate types. However, avoid using any as it defeats the purpose of type safety. If you must use a loose type, prefer unknown or never.

// Avoid
let value: any;

// Prefer
let value: unknown;
Enter fullscreen mode Exit fullscreen mode

6. Leverage Utility Types

TypeScript provides utility types that can simplify your code and make it more readable. For instance, Partial<T>, Pick<T, K>, and Omit<T, K> are useful for handling component props and state.

interface User {
  id: string;
  name: string;
  email: string;
}

type UserPreview = Omit<User, 'email'>;
Enter fullscreen mode Exit fullscreen mode

7. Use Default Props and TypeScript Union Types

Define default props directly in the component function and use union types for more flexible prop definitions:

interface AlertProps {
  message: string;
  type?: 'success' | 'error' | 'warning';
}

const Alert: FC<AlertProps> = ({ message, type = 'success' }) => (
  <div className={`alert alert-${type}`}>
    {message}
  </div>
);
Enter fullscreen mode Exit fullscreen mode

8. Avoid Overuse of any

TypeScript’s any type disables type checking, which can lead to runtime errors. Instead, try to use more specific types or use unknown and cast to specific types when necessary.

const handleEvent = (event: Event): void => {
  const target = event.target as HTMLInputElement;
  console.log(target.value);
};
Enter fullscreen mode Exit fullscreen mode

9. Use Type Declarations for External Libraries

When using third-party libraries, ensure you install type declarations if available:

npm install --save @types/react-router-dom
Enter fullscreen mode Exit fullscreen mode

For libraries without type definitions, consider creating custom type declarations.

10. Consistent Naming Conventions

Follow consistent naming conventions for types and interfaces. Use PascalCase for type and interface names:

interface UserProfile {
  id: string;
  name: string;
  email: string;
}

type UserId = UserProfile['id'];
Enter fullscreen mode Exit fullscreen mode

11. Use ESLint and Prettier

Integrate ESLint and Prettier into your project to maintain code quality and consistency. Install the necessary packages and configure them to work with TypeScript:

npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
Enter fullscreen mode Exit fullscreen mode

Create or update your .eslintrc.js and .prettierrc files with appropriate configurations.

12. Optimize Vite Configuration

Vite’s configuration can be customized in the vite.config.ts file. Ensure you have appropriate settings for TypeScript:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': '/src',
    },
  },
  esbuild: {
    jsxInject: `import React from 'react'`,
  },
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

Using TypeScript in React with Vite combines the strengths of both tools, enhancing the development experience with fast builds and strong type safety. By following these best practices, you can build robust, maintainable, and scalable React applications. Adopting these guidelines will help you harness the full potential of TypeScript and Vite, making your development process more efficient and enjoyable.

Top comments (0)