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 insrc/
to.ts
and.tsx
respectively. -
Run
npm start
, and CRA will generatetsconfig.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
andbabel.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 likefight()
,explore()
. -
A Ninja class can extend Warrior and add its own properties/methods like
shuriken
andthrowShuriken()
, 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
-
Create a new file:
Inside yoursrc
folder, create a new file calledHoc.tsx
. -
Setup:
Import React and Component:
import React, { Component } from 'react';
-
Move state and types:
Cut theinitialState
and theState
type definitions fromApp.tsx
and paste them intoHoc.tsx
. This cleans upApp.tsx
and centralizes state logic inside the HOC. -
Define the HOC:
Create a function calledmessageHoc
that takes a component (WrappedComponent
) of typeany
:
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.
-
Export the HOC:
ExportmessageHoc
as the default export:
export default messageHoc;
-
Refactor the message component:
In yourMessage.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;
-
Wrap the component:
Back inMessage.tsx
, importmessageHoc
:
import messageHoc from './Hoc';
Then wrap the Example
component with the HOC and export it as Message
:
const Message = messageHoc(Example);
export { Message };
-
Result:
-
The HOC manages state and injects props into
Example
. -
Example
simply renders those props.
-
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
Post a Comment