React + TypeScript Setup and Usage Guide

 

React + TypeScript Setup and Usage Guide

1. Create React App with TypeScript

npx create-react-app react-ts-app --template typescript
cd react-ts-app
npm start
  • This sets up a React project with TypeScript ready out of the box.

  • tsconfig.json will be auto-generated.

  • The default source files will be .tsx instead of .js.


2. Manual Setup (if starting with JavaScript project and adding TS)

If you started with a plain React app and want to add TypeScript later:

npm install --save typescript @types/node @types/react @types/react-dom @types/jest
  • Rename .js and .jsx files in src/ to .ts and .tsx respectively.

  • Run npm start, and CRA will generate tsconfig.json automatically.

  • React Scripts version 2.1.1+ supports this smooth TS integration.


3. Typical package.json scripts

"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
  "test": "react-scripts test",
  "eject": "react-scripts eject"
}
  • If you're doing your own webpack/babel setup, scripts will differ accordingly.


4. Babel, Webpack, and other packages (if custom setup)

npm install express mongodb
npm install -D webpack webpack-cli babel-loader @babel/cli @babel/core @babel/preset-env @babel/preset-react @babel/plugin-proposal-class-properties
npm install -D nodemon eslint babel-eslint eslint-plugin-react
npm install prop-types
  • Create webpack.config.js and babel.config.js for custom bundling.

  • ESLint config in .eslintrc.js.


5. Basic TypeScript Types in React Components

Example usage inside a .tsx file:

import React from 'react';

const ExampleComponent: React.FC = () => {
  // String
  let firstValue: string = "Manny";

  // Number
  let age: number = 34;

  // Boolean
  let isLoggedIn: boolean = true;

  // Array (method 1)
  let numbers: number[] = [2, 3, 56];

  // Array (method 2)
  let names: Array<string> = ["2", "3", "Manny"];

  return (
    <div>
      <p>The value "{firstValue}" is of string type.</p>
      <p>The value {age} is of number type.</p>
      <p>Logged in: {isLoggedIn ? "Yes" : "No"}</p>
      <p>Numbers: {numbers.join(", ")}</p>
      <p>Names: {names.join(", ")}</p>
    </div>
  );
};

export default ExampleComponent;
  • TypeScript will enforce correct types and throw errors for mismatches.

  • This improves code quality and catches bugs early.


6. Tips for smooth TypeScript integration

  • Rename files to .tsx for React components with JSX.

  • Use type annotations (: string, : number, etc.) for variables and props.

  • Install @types packages for libraries (React, Node, Jest).

  • Use React.FC or define explicit types for component props.

  • Start simple and gradually add more strict typing.


React + TypeScript Update

Other Complex Types in TypeScript

Tuple

  • A tuple is a typed array with a fixed number of elements whose types are known.

  • You can mix multiple types but must specify them in order.

  • Example:

    let aTuple: [string, number] = ["Manny", 34];
    
    console.log(aTuple[0]); // Manny (string)
    console.log(aTuple[1]); // 34 (number)
    
  • Useful when you want a fixed-length array with specific types in each position.

Enum

  • Enums let you define a set of named constants.

  • Similar to objects but with special syntax and behavior.

  • Example:

    enum Codes {
      First = 1,
      Second = 2
    }
    
    console.log(Codes.First);  // 1
    console.log(Codes.Second); // 2
    
  • You can assign values explicitly or let them auto-increment.

Any

  • any disables type checking for that variable.

  • Use sparingly because it removes the benefits of TypeScript.

  • Example:

    let firstName: any = "Manny";
    firstName = 123; // No error, but unsafe
    
  • Useful for prototyping or migrating JavaScript code.

Void

  • Used for functions that don't return a value.

  • Example:

    function warning(): void {
      console.log("Warning");
    }
    
  • Indicates the function has no return type.


Functional / Stateless Component Intro with TypeScript

  • Functional components are plain JavaScript functions returning JSX.

  • TypeScript mainly adds type annotations to props and return types.

  • Basic example of a functional component with no props:

import React from 'react';

function Message(): JSX.Element {
  return <p>This is a quick message.</p>;
}

export default Message;
  • Import and use in App.tsx:

import React from 'react';
import Message from './Message';

function App(): JSX.Element {
  return (
    <div>
      <Message />
    </div>
  );
}

export default App;
  • You can extend this by adding typed props and more advanced features.

React + TypeScript: Class / Stateful Components


What is a Class? (Conceptual Intro)

  • Class is a blueprint to create objects with properties and methods.

  • Introduced in ES6 JavaScript and commonly used in TypeScript.

  • Allows you to define reusable blueprints with data (properties) and behavior (methods).

  • Example analogy:

    • A Warrior class might have properties like strength, agility and methods like fight(), explore().

    • A Ninja class can extend Warrior and add its own properties/methods like shuriken and throwShuriken(), but still use Warrior’s functionality.

  • Classes help avoid duplicate code and keep your app organized.

  • In React, class components extend React.Component to gain:

    • Access to state management.

    • Lifecycle methods (e.g., componentDidMount).

  • General recommendation: Prefer functional components unless you specifically need features from class components (like complex lifecycle handling).


Class Component Syntax with TypeScript

Basic structure

import React, { Component } from 'react';

class MyComponent extends Component<any, any> {
  // component state and methods go here

  // Lifecycle method example
  componentDidMount() {
    console.log('Component did mount');
  }

  render() {
    return <div>Hello from Class Component</div>;
  }
}

export default MyComponent;
  • extends Component<PropsType, StateType>

    • Here, any is used temporarily for types of props and state.

    • You can replace any with proper interfaces for better type safety.


Passing Types to the Component

  • Use generics with Component<> to specify props and state types:

interface MyProps {
  title: string;
}

interface MyState {
  count: number;
}

class MyComponent extends Component<MyProps, MyState> {
  constructor(props: MyProps) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    return <h1>{this.props.title} - {this.state.count}</h1>;
  }
}

Lifecycle Methods Example

  • Common lifecycle methods:

Method When it runs
componentWillMount Before component mounts (legacy, avoid in new React versions)
componentDidMount After component mounts (good for API calls, setup)
componentDidUpdate After component updates
componentWillUnmount Before component unmounts (cleanup)

Example with componentWillMount & componentDidMount

class MyComponent extends Component<any, any> {

  componentWillMount() {
    console.log('Almost there');  // Runs before render (legacy)
  }

  componentDidMount() {
    console.log('Finally... hello');  // Runs after component renders
  }

  render() {
    return <div>Check the console for lifecycle logs</div>;
  }
}
  • When you run this and open browser console:

    • "Almost there" logs first (before render).

    • "Finally... hello" logs after render.


Notes on Usage

  • componentWillMount is considered legacy and not recommended in modern React.

  • Use componentDidMount to:

    • Fetch data from APIs.

    • Perform side effects that should happen once after the first render.

  • Class components give you lifecycle hooks and state but are less used today compared to hooks in functional components.

React + TypeScript: Interfaces and Typed State in Class Components


1. Introduction to Interfaces for Props

  • Interfaces define the shape of data objects and the types of their properties.

  • They are used only at compile time by TypeScript to validate types—no runtime impact.

  • Interfaces ensure you pass the correct prop types to components, catching errors early.

Example: Creating a UserMessage Interface for Props

interface UserMessage {
  name: string;
  message: string;
}
  • Use this interface in a React class or functional component:

type Props = UserMessage;

class Message extends React.Component<Props> {
  render() {
    return (
      <p>{this.props.name}: {this.props.message}</p>
    );
  }
}
  • Passing props in the parent component:

<Message name="Manny" message="This is a simple message." />
  • If props don't match the interface (e.g., wrong types or missing), TypeScript will show compile errors.


2. Defining State with Types in a React Class Component

  • You can define the shape and types of your component’s state to get full type safety.

  • One approach is to define an initial state object with sample values.

  • Then define a State type using TypeScript's typeof keyword for automatic inference:

const initialState = {
  name: "Manny",
  message: "TypeScript is cool"
};

type State = Readonly<typeof initialState>;
  • Using Readonly ensures the state is immutable from outside the component.

  • Pass the State type to your component class:

class App extends React.Component<{}, State> {
  readonly state: State = initialState;

  render() {
    return (
      <Message name={this.state.name} message={this.state.message} />
    );
  }
}
  • The state is now typed, so any accidental type mismatch (e.g., setting name as a number) will cause a compile error.


3. Summary and Benefits

  • Interfaces for props define what data your components expect and validate correct usage.

  • Typed state ensures your React component’s state stays consistent and catches bugs early.

  • Using Readonly for state helps avoid unintended mutations.

  • TypeScript’s type checking at compile time prevents runtime errors and improves code safety.

  • Properly typed props and state improve code maintainability and developer experience.

Here’s an updated, cleaned-up version of your transcript content for Reviewing best practices with interfaces and Implementing HOCs in TypeScript, with improved clarity, formatting, and flow:


Review Best Practices for Interfaces

  • Instructor:
    Let's quickly review best practices for interfaces.

Interfaces don't appear in your compiled JavaScript code—they exist solely to help you define and shape your data, especially useful for props in React components.

As we've seen, you define your props first in an interface, then pass that interface into your functional component. If the props passed to the component don’t match the defined types, TypeScript will give you an error. This is a great way to enforce expected data shapes and prevent runtime errors or security issues.

Optional Properties:
Sometimes you might have optional properties. For example, if you have a Warrior interface with properties like token or money, but money might not always be present, you can mark it optional by adding a question mark:

interface Warrior {
  token: string;
  money?: number;
}

This way, if money is missing, it's still valid.

Readonly Properties:
You may have seen the readonly keyword used in interfaces. This means once a property is set, it cannot be changed. It’s like creating a constant property within your object. This is useful when you want to ensure props are immutable after initialization.

Function Types in Interfaces:
Interfaces can also define the shape of functions. For example, you can specify the argument and return types for functions to ensure they conform to expected signatures. If you don’t follow the function shape, TypeScript will warn you.

Implementing Higher-Order Components (HOCs) in TypeScript

  1. Create a new file:
    Inside your src folder, create a new file called Hoc.tsx.

  2. Setup:
    Import React and Component:

import React, { Component } from 'react';
  1. Move state and types:
    Cut the initialState and the State type definitions from App.tsx and paste them into Hoc.tsx. This cleans up App.tsx and centralizes state logic inside the HOC.

  2. Define the HOC:
    Create a function called messageHoc that takes a component (WrappedComponent) of type any:

const messageHoc = (WrappedComponent: any) => {
  return class HOC extends Component<{}, State> {
    readonly state = initialState;

    render() {
      return (
        <WrappedComponent
          name={this.state.name}
          message={this.state.message}
        />
      );
    }
  }
};

This HOC holds the state and passes it down as props to the wrapped component.

  1. Export the HOC:
    Export messageHoc as the default export:

export default messageHoc;
  1. Refactor the message component:
    In your Message.tsx:

  • Remove the interface and state definitions since the HOC handles them.

  • Create a simple functional component that accepts props of type any and renders them:

const Example = (props: any) => (
  <p>{props.name}, {props.message}</p>
);

export default Example;
  1. Wrap the component:
    Back in Message.tsx, import messageHoc:

import messageHoc from './Hoc';

Then wrap the Example component with the HOC and export it as Message:

const Message = messageHoc(Example);
export { Message };
  1. Result:

  • The HOC manages state and injects props into Example.

  • Example simply renders those props.

  1. Test:
    Run your app and use React Developer Tools in Chrome to inspect the component tree. You'll see the HOC wrapping your component with the state injected as props.


This setup shows how HOCs can encapsulate logic like state management and pass it down to wrapped components, all with TypeScript's static typing benefits.

For more advanced usage and best practices, I recommend checking out the official React and TypeScript documentation.

HOCs Best Practices

  • Instructor:
    There’s a lot more to explore with Higher-Order Components (HOCs), and what we covered here is just a brief introduction with TypeScript.

Recommendations:

  • Start Simple:
    Get comfortable with HOCs in plain React (without TypeScript) first. Once confident, introduce TypeScript to add type safety and catch errors early.

  • Avoid Modifying the HOC or Prototype:
    Don’t change the internal implementation or prototype of your HOC after creating it, as this can unexpectedly affect all components using it.

  • Passing Props:
    If you need to pass props down to the wrapped component, always pass them explicitly through the HOC, like in our example. Do not inject props directly inside the wrapped component. Only inject props from the HOC’s own state or logic.

  • Avoid Using HOCs Inside Render Methods:
    Don’t create or use HOCs inside a component’s render method. Doing so can interfere with React’s rendering optimizations and degrade performance.

  • TypeScript Tip:
    If you encounter confusing type errors, always refer to the official React and TypeScript documentation or perform a targeted web search. Most issues can be resolved this way.

  • When to Use HOCs:
    Use HOCs when your component logic becomes too complex or repetitive. They help abstract and reuse behavior, enabling better composition and making your app easier to maintain by breaking it down into smaller, more manageable components.


Comments

Popular posts from this blog

React Full Stack Project Design Build Launch

React useState Hook – Complete Guide

Building an AI-Powered App with Next.js, Tailwind CSS, and OpenAI API