SoFunction
Updated on 2025-04-07

Best practices and tips for using TypeScript in React

introduction

Using TypeScript in React projects can significantly improve the maintainability and readability of your code, and provide powerful type checking capabilities to reduce runtime errors. Here are some best practices and tips for elegantly integrating TypeScript into your React project.

1. Create a React TypeScript project

You can use the Create React App to create a TypeScript project:

npx create-react-app my-app --template typescript

2. Configure TypeScript

Make sure yoursThe file is configured correctly. Here is a common oneConfiguration:

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "react-jsx"
  },
  "include": ["src"]
}

3. Basic type annotation

Use TypeScript to define the props and state of the component. Here is a simple example:

Function Components

import React from 'react';

interface GreetingProps {
  name: string;
}

const Greeting: <GreetingProps> = ({ name }) => {
  return <h1>Hello, {name}!</h1>;
};

export default Greeting;

Class Components

import React, { Component } from 'react';

interface GreetingProps {
  name: string;
}

interface GreetingState {
  count: number;
}

class Greeting extends Component<GreetingProps, GreetingState> {
  constructor(props: GreetingProps) {
    super(props);
     = {
      count: 0,
    };
  }

  render() {
    return (
      <div>
        <h1>Hello, {}!</h1>
        <p>Count: {}</p>
      </div>
    );
  }
}

export default Greeting;

4. Use Hooks

Use TypeScript to type Hooks:

useState

import React, { useState } from 'react';

const Counter:  = () => {
  const [count, setCount] = useState<number>(0);

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

export default Counter;

useReducer

import React, { useReducer } from 'react';

interface State {
  count: number;
}

type Action = { type: 'increment' } | { type: 'decrement' };

const initialState: State = { count: 0 };

const reducer = (state: State, action: Action): State => {
  switch () {
    case 'increment':
      return { count:  + 1 };
    case 'decrement':
      return { count:  - 1 };
    default:
      return state;
  }
};

const Counter:  = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>{}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
};

export default Counter;

5. Context API

Use TypeScript to type Context:

import React, { createContext, useContext, useState, ReactNode } from 'react';

interface AuthContextType {
  user: string | null;
  login: (username: string) => void;
  logout: () => void;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export const AuthProvider: <{ children: ReactNode }> = ({ children }) => {
  const [user, setUser] = useState<string | null>(null);

  const login = (username: string) => {
    setUser(username);
  };

  const logout = () => {
    setUser(null);
  };

  return (
    < value={{ user, login, logout }}>
      {children}
    </>
  );
};

export const useAuth = (): AuthContextType => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
};

6. Advanced Components (HOCs)

When defining advanced components, you need to handle the passed props and enhanced props correctly.

import React, { ComponentType } from 'react';

interface WithLoadingProps {
  loading: boolean;
}

const withLoading = <P extends object>(
  WrappedComponent: ComponentType<P>
): <P & WithLoadingProps> => ({ loading, ...props }) => {
  if (loading) {
    return <div>Loading...</div>;
  }
  return <WrappedComponent {...(props as P)} />;
};

export default withLoading;

7. Type declaration file

If the library you are using does not have a type definition file, you can create a type declaration file. For example, it can besrc/typesAdd one to the folderdocument:

// src/types/
declare module 'my-library' {
  export function myFunction(): string;
}

8. Types of using third-party libraries

Install and use the type definition of third-party libraries. For example,lodashLibrary

yarn add lodash
yarn add @types/lodash --dev
# or
npm install lodash
npm install @types/lodash --save-dev

Then use in the code:

import _ from 'lodash';

const result = _.chunk(['a', 'b', 'c', 'd'], 2);

9. Configure ESLint and Prettier

Use ESLint and Prettier for code quality and style checks:

Install ESLint and Prettier

yarn add eslint eslint-plugin-react @typescript-eslint/parser @typescript-eslint/eslint-plugin --dev
yarn add prettier eslint-config-prettier eslint-plugin-prettier --dev
# or
npm install eslint eslint-plugin-react @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev
npm install prettier eslint-config-prettier eslint-plugin-prettier --save-dev

Configure ESLint

Create in the project root directory.document:

 = {
  parser: '@typescript-eslint/parser',
  extends: [
    'plugin:react/recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier',
    'prettier/@typescript-eslint',
    'prettier/react'
  ],
  plugins: ['@typescript-eslint', 'react', 'prettier'],
  parserOptions: {
    ecmaVersion: 2020,
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
    },
  },
  settings: {
    react: {
      version: 'detect',
    },
  },
  rules: {
    'prettier/prettier': 'error',
  },
};

Configure Prettier

Create in the project root directory.prettierrcdocument:

{
  "singleQuote": true,
  "trailingComma": "all",
  "printWidth": 80
}

Summarize

By using TypeScript and combined with React's functions, the robustness and maintainability of the code can be greatly improved. The above practices include basic type annotations, using Hooks, Context API, advanced components, etc. Good tools and configurations such as ESLint and Prettier can also ensure consistent code style and help identify potential problems. Through these measures, TypeScript can be used gracefully in React projects, improving development efficiency and code quality.

Follow-up question: What are the common TS methods used in React?

When using TypeScript in React, there are many common patterns and tricks that can help you manage types and components more efficiently. Here are some common methods and best practices for using TypeScript in React:

1. Basic type annotation

Function Components

Define interfaces for props of function components and useTo define function components:

import React from 'react';

interface GreetingProps {
  name: string;
}

const Greeting: <GreetingProps> = ({ name }) => {
  return <h1>Hello, {name}!</h1>;
};

export default Greeting;

Class Components

Define interfaces for props and state of class components and use them in class components:

import React, { Component } from 'react';

interface GreetingProps {
  name: string;
}

interface GreetingState {
  count: number;
}

class Greeting extends Component<GreetingProps, GreetingState> {
  constructor(props: GreetingProps) {
    super(props);
     = {
      count: 0,
    };
  }

  render() {
    return (
      <div>
        <h1>Hello, {}!</h1>
        <p>Count: {}</p>
        <button onClick={() => ({ count:  + 1 })}>
          Increment
        </button>
      </div>
    );
  }
}

export default Greeting;

2. Use Hooks

useState

Define the initial value and type for useState:

import React, { useState } from 'react';

const Counter:  = () => {
  const [count, setCount] = useState<number>(0);

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

export default Counter;

useEffect

Define types and dependencies for useEffect:

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

const Timer:  = () => {
  const [time, setTime] = useState<Date>(new Date());

  useEffect(() => {
    const timer = setInterval(() => {
      setTime(new Date());
    }, 1000);
    return () => clearInterval(timer);
  }, []);

  return <div>{()}</div>;
};

export default Timer;

useReducer

Define state and action types for useReducer:

import React, { useReducer } from 'react';

interface State {
  count: number;
}

type Action = { type: 'increment' } | { type: 'decrement' };

const initialState: State = { count: 0 };

const reducer = (state: State, action: Action): State => {
  switch () {
    case 'increment':
      return { count:  + 1 };
    case 'decrement':
      return { count:  - 1 };
    default:
      return state;
  }
};

const Counter:  = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>{}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
};

export default Counter;

3. Use the Context API

Define the type and default values ​​for the Context:

import React, { createContext, useContext, useState, ReactNode } from 'react';

interface AuthContextType {
  user: string | null;
  login: (username: string) => void;
  logout: () => void;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export const AuthProvider: <{ children: ReactNode }> = ({ children }) => {
  const [user, setUser] = useState<string | null>(null);

  const login = (username: string) => {
    setUser(username);
  };

  const logout = () => {
    setUser(null);
  };

  return (
    < value={{ user, login, logout }}>
      {children}
    </>
  );
};

export const useAuth = (): AuthContextType => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
};

4. Use Advanced Components (HOCs)

Define props and types of advanced components:

import React, { ComponentType } from 'react';

interface WithLoadingProps {
  loading: boolean;
}

const withLoading = <P extends object>(
  WrappedComponent: ComponentType<P>
): <P & WithLoadingProps> => ({ loading, ...props }) => {
  if (loading) {
    return <div>Loading...</div>;
  }
  return <WrappedComponent {...(props as P)} />;
};

export default withLoading;

5. Use refs

Define the type for refs:

import React, { useRef, useEffect } from 'react';

const FocusInput:  = () => {
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if () {
      ();
    }
  }, []);

  return <input ref={inputRef} type="text" />;
};

export default FocusInput;

6. Define complex objects and enumerations

Define complex object types and use enumerations:

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

enum UserRole {
  Admin = 'admin',
  User = 'user',
  Guest = 'guest',
}

const UserProfile: <{ user: User; role: UserRole }> = ({ user, role }) => {
  return (
    <div>
      <h1>{}</h1>
      <p>Email: {}</p>
      <p>Role: {role}</p>
    </div>
  );
};

export default UserProfile;

7. Type inference and union type

Type inference and union types using TypeScript:

type Status = 'loading' | 'success' | 'error';

interface LoadingState {
  status: 'loading';
}

interface SuccessState {
  status: 'success';
  data: string;
}

interface ErrorState {
  status: 'error';
  error: string;
}

type State = LoadingState | SuccessState | ErrorState;

const FetchData:  = () =&gt; {
  const [state, setState] = useState&lt;State&gt;({ status: 'loading' });

  useEffect(() =&gt; {
    // Simulate data request    setTimeout(() =&gt; {
      setState({ status: 'success', data: 'Hello World' });
    }, 1000);
  }, []);

  if ( === 'loading') {
    return &lt;div&gt;Loading...&lt;/div&gt;;
  }

  if ( === 'error') {
    return &lt;div&gt;Error: {}&lt;/div&gt;;
  }

  return &lt;div&gt;Data: {}&lt;/div&gt;;
};

export default FetchData;

Summarize

By using these common TypeScript methods, types and components in a React application can be managed more gracefully. By correctly using TypeScript's type annotation, type inference, and advanced type features, the maintainability, readability and security of your code can be significantly improved. Here are some key points:

  • Basic type annotations for function components and class components
  • Type definition using Hooks (such as useState, useEffect, useReducer)
  • Type definition of Context API
  • Type definition of advanced components (HOC)
  • Type definition using refs
  • The use of complex objects and enumerations
  • Type inference and the use of union types

Through these methods, you can take advantage of the power of TypeScript in React to write more robust and reliable code.

The above is the detailed content of the best practices and techniques for React using TypeScript. For more information about React using TypeScript, please follow my other related articles!