TypeScript Integration with React: Setup and Typing Components

Creating a TypeScript–powered React Project

Create React App (CRA) can generate a project with all TypeScript tooling preconfigured using the --template typescript flag. For faster package downloads you may switch to a mirror registry:

npm config set registry https://registry.npmmirror.com

Then bootstrap the application:

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

If you prefer Yarn and are targeting React 19+, the command is:

yarn create react-app my-ts-react --template typescript

After creation the directory structure differs from a plain JavaScript project in three ways:

  • tsconfig.json appears at the project root, holding the TypeScript compiler options.
  • Component files use the .tsx extension.
  • The src folder contains react-app-env.d.ts – a triple‑slash reference that loads type declarations from react-scripts.
/// <reference types="react-scripts" />

This declaration imports types for React, ReactDOM, Node, and static assets like images and SVGs, so you can safely import them with out additional configuration.

Understanding tsconfig.json

The tsconfig.json file dictates how the TypeScript compiler handles your code. Below is a representative configuration from a CRA‑based project:

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

Important notes about compiler behavior:

  • Runing tsc hello.ts --target es6 overrides tsconfig.json because an input file is specified.
  • Executing tsc without arguments picks up the tsconfig.json from the project root.

It is recommended to rely on the configuration file for day‑to‑day development.

Typing React Components

React provides type definitions through the @types/react and @types/react-dom packages, which are automatically included in a CRA TypeScript template. We'll explore common typing patterns for function and class components.

Function Components

Define props with an interface or type alias. The component can be annotated using React.FC or simply by typing its parameters directly.

interface UserProfileProps {
  fullName: string;
  age?: number;
}

// Using React.FC generic
const UserProfile: React.FC<UserProfileProps> = ({ fullName, age }) => (
  <div>Hello, I'm {fullName}, {age ?? 'unknown'} years old.</div>
);

// Alternative: inline parameter typing
const UserProfileInline = ({ fullName, age }: UserProfileProps) => (
  <div>Hello, I'm {fullName}, {age ?? 'unknown'} years old.</div>
);

Provide default values through destructuring:

const UserProfileDefaultAge = ({ fullName, age = 30 }: UserProfileProps) => (
  <div>Hello, I'm {fullName}, {age} years old.</div>
);

Event handlers require explicit types. A useful technique is to write the handler inline and hover over the parameter to let TypeScript infer the correct event type.

const UserProfileWithActions = ({ fullName }: UserProfileProps) => {
  const handleButtonClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    console.log('Button clicked', e.currentTarget);
  };

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    console.log(e.target.value);
  };

  return (
    <div>
      {fullName}
      <button onClick={handleButtonClick}>Like</button>
      <input onChange={handleInputChange} />
    </div>
  );
};

Class Components

Class components can define generic parameters for props and state. The following snippet illustrates all combinations and a complete counter example.

interface CounterState {
  tally: number;
}
interface CounterProps {
  initialValue?: number;
}

class Counter extends React.Component<CounterProps, CounterState> {
  state: CounterState = {
    tally: this.props.initialValue ?? 0
  };

  increment = () => {
    this.setState(prevState => ({ tally: prevState.tally + 1 }));
  };

  render() {
    return (
      <div>
        Count: {this.state.tally}
        <button onClick={this.increment}>+1</button>
      </div>
    );
  }
}

Other common patterns:

// No props, no state
class EmptyShell extends React.Component {}

// Only props
class PropsOnly extends React.Component<CounterProps> {}

// Only state
class StateOnly extends React.Component<{}, CounterState>

Default property values can be defined via static defaultProps (though this is less common with modern TypeScript) or directly in the render logic using default destructuring as shown above.

Tags: TypeScript React CRA tsconfig JSX

Posted on Fri, 08 May 2026 23:06:38 +0000 by Xeon