TypeScript and react : A Complete guide For Beginners
Introduction to TypeScript and React
So you heard about React and TypeScript. Maybe you are familiar with JavaScript and how it’s used to build interactive web applications. Well TypeScript is like a supercharged version of JavaScript. And React is a popular JavaScript library for building user interfaces.
Now why would you want to use TypeScript with React ? Good question!. TypeScript adds some extra goodies to JavaScript that can make your life as a developer a whole lot easier.
One of the main advantages of TypeScript is that it brings strong typing to JavaScript. What does that mean ? Well it means that you can define the types of variables, function parameters and return values in your code. This helps catch errors early on in the development process before they can cause any error at runtime.
With TypeScript you also get better tooling support. Most modern code editors and IDEs have built-in support for TypeScript which means you get features like code completion, type checking and automatic refactoring right out of the box.
Another big advantage of TypeScript is improved code readability and maintainability. By explicitly defining types in your code. it becomes easier for other developers to understand what’s going on. This can be especially helpfull when working on larger projects with lots of moving parts.
Now let’s talk about why TypeScript plays so nicely with React. React is all about building reusable UI components and TypeScript’s strong typing makes it a perfect match. With TypeScript you can define the props and state of your React components upfront that making them easier to use and less error-prone.
TypeScript brings strong typing, better tooling support and improved code readability that thing making it a great choice for React development. By combining the power of TypeScript with the simplicity of React now you can build robust and maintainable web applications with confidence.
Setting Up TypeScript with Create React App
Alright let’s setting up a new React project with TypeScript using Create React App. This tool simplifies the process of creating React applications and integrates seamlessly with TypeScript. Here’s how you can get started:
Create a New React Project
you can create a new React project with TypeScript support by running the following command:
npx create-react-app typescript-react --template typescript
Replace “typescript-react” with the desired name for your project. This command creates a new directory called “typescript-react” (or whatever name you provided) containing a pre-configured React project with TypeScript support.
After the project has been created. now navigate to the project directory using the following command:
cd typescript-react
Once inside your project directory now you can start the development server by running:
npm start
This command launches the development server and opens your new React application in your default web browser.
Additional Setup (Optional)
- Customizing Configuration : Create React App abstracts away much of the configuration complexity but you can still customize various aspects of your project by ejecting the configuration. But always keep in mind that ejecting is a one-way operation and should be approached with caution.
- Adding Additional Dependencies : Depending on your project requirements you may need to install additional dependencies such as React Router for routing, Axios for making HTTP requests or any other packages you need to upgrade your application’s functionality.
typeScript basic for react development
Before we go much deeper lets take a look into TypeScript Basics
what is types in typeScript ?
TypeScript allows you to specify types for variables, parameters and return values. This helps catch errors and make sure type safety in your code. In React you might use types to define the props and state of your components. For example:
type ButtonProps = {
onClick: () => void;
text: string;
};
const Button: React.FC<ButtonProps> = ({ onClick, text }) => {
return <button onClick={onClick}>{text}</button>;
};
what is Interfaces in typeScript ?
Interfaces are similar to types but are typically used to define object shapes. They can also be extended and implemented. In React interfaces are commonly used to define the structure of component props and state.
interface Todo {
id: number;
text: string;
completed: boolean;
}
interface TodoListProps {
todos: Todo[];
}
const TodoList: React.FC<TodoListProps> = ({ todos }) => {
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
};
what is Enums in typeScript ?
Enums allow you to define a set of named constants. They are handy for representing a fixed set of values. In React enums can be used for defining status or type indicators:
enum Status {
Pending,
Completed,
InProgress,
}
const getStatusMessage = (status: Status): string => {
switch (status) {
case Status.Pending:
return 'Pending';
case Status.Completed:
return 'Completed';
case Status.InProgress:
return 'In Progress';
default:
return '';
}
};
what is Generics in typeScript ?
Generics give you ability to create reusable components, functions and data structures that work with a variety of types. In React generics can be used in components to create flexible and reusable components:
interface BoxProps<T> {
content: T;
}
const Box = <T extends {}>({ content }: BoxProps<T>) => {
return <div>{content}</div>;
};
what is Type Inference in typeScript ?
TypeScript’s type inference automatically deduces the types of variables and expressions when they are initialized. This helps reduce the need for explicit type annotations:
const message = 'Hello, world!'; // TypeScript infers message to be of type string
what is Type Assertions in typeScript ?
Type assertions allow you to override TypeScript’s inferred or statically determined types. They are usefull when you know more about a value’s type than TypeScript does:
const someValue: any = 'this is a string';
const strLength: number = (someValue as string).length;
These TypeScript fundamentals are essential for building robust and type-safe React applications. Understanding and applying them will help you write cleaner and more maintainable code.
React Components with TypeScript
When building React applications with TypeScript. Defining components with strong typings for props and state is important for maintaining code clarity and type safety. Let’s explore how to do this for both functional and class components:
Functional Components in typeScript
Functional components are simple and concise that making them a popular choice in React development. Here’s how you can define a functional component with TypeScript:
import React from 'react';
type GreetingProps = {
name: string;
};
const Greeting: React.FC<GreetingProps> = ({ name }) => {
return <div>Hello, {name}!</div>;
};
export default Greeting;
In this example GreetingProps
defines the type of props expected by the Greeting component. The React.FC
type indicates that Greeting
is a functional component that accepts GreetingProps
.
Props and State with Strong Typings in typeScript
Make sure strong typings for props and state helps prevent bugs and improves code maintainability. Here’s an example of a component with both props and state:
import React, { Component } from 'react';
type UserInfo = {
name: string;
age: number;
};
type UserInfoProps = {
user: UserInfo;
};
type UserInfoState = {
isEditing: boolean;
};
class UserInfoComponent extends Component<UserInfoProps, UserInfoState> {
state: UserInfoState = {
isEditing: false,
};
render() {
const { name, age } = this.props.user;
const { isEditing } = this.state;
return (
<div>
{isEditing ? (
<input type="text" value={name} />
) : (
<p>Name: {name}, Age: {age}</p>
)}
<button onClick={() => this.setState({ isEditing: !isEditing })}>
{isEditing ? 'Save' : 'Edit'}
</button>
</div>
);
}
}
export default UserInfoComponent;
In this example UserInfoProps
defines the type of props expected by the component while UserInfoState
specifies the type of its state. This make sure that the component receives the correct props and manages its state appropriately.
By defining React components with TypeScript. You can take all the benefits of static typing to catch errors early and increase the maintainability of your codebase
react hooks and typeScript
React Hooks transformed how developers manage state and side effects in functional components. Integrating TypeScript with React Hooks adds an extra layer of type safety and clarity to your code. Let’s explore how to use React Hooks with TypeScript. We will cover only common hooks like useState, useEffect, useContext, useRef and custom hooks.
if you Dont know about that you can find all detailed guide of React Hooks
example of useEffect hook with typeScript
import React, { useState, useEffect } from 'react';
const DataFetching: React.FC = () => {
const [data, setData] = useState<string>('');
useEffect(() => {
// Fetch data
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
}, []);
return <div>Data: {data}</div>;
};
export default DataFetching;
Here we fetch data from an API in the useEffect hook and update state accordingly.
example of useState hook with typeScript
import React, { useState } from 'react';
const Counter: React.FC = () => {
const [count, setCount] = useState<number>(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default Counter;
In this example useState(0)
initializes state with a number type and a default value of 0.
example of useContext hook with typeScript
import React, { useContext } from 'react';
type ThemeContextType = {
darkMode: boolean;
toggleDarkMode: () => void;
};
const ThemeContext = React.createContext<ThemeContextType>({
darkMode: false,
toggleDarkMode: () => {},
});
const ToggleButton: React.FC = () => {
const { darkMode, toggleDarkMode } = useContext(ThemeContext);
return (
<button onClick={toggleDarkMode}>
{darkMode ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
</button>
);
};
export default ToggleButton;
The useContext
hook provides access to the context value defined by a nearest matching Context.Provider
.
example of useRef hook with typeScript
import React, { useRef } from 'react';
const InputFocus: React.FC = () => {
const inputRef = useRef<HTMLInputElement>(null);
const focusInput = () => {
inputRef.current?.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</div>
);
};
export
State Management with TypeScript
State management libraries like Redux and MobX provide robust solutions for managing application state in complex React applications. When combined with TypeScript these libraries offer increase type safety and help prevent common errors. Let’s explore how to define typed actions, reducers, selectors and store setups with TypeScript for both Redux and MobX:
redux with typeScript
Typed Actions
Define action types as TypeScript enums or string constants:
enum ActionTypes {
INCREMENT = 'INCREMENT',
DECREMENT = 'DECREMENT',
}
type IncrementAction = {
type: ActionTypes.INCREMENT;
};
type DecrementAction = {
type: ActionTypes.DECREMENT;
};
type Action = IncrementAction | DecrementAction;
Reducers
Define a typed reducer function to handle actions
import { Reducer } from 'redux';
type CounterState = {
count: number;
};
const initialState: CounterState = {
count: 0,
};
const counterReducer: Reducer<CounterState, Action> = (state = initialState, action) => {
switch (action.type) {
case ActionTypes.INCREMENT:
return { ...state, count: state.count + 1 };
case ActionTypes.DECREMENT:
return { ...state, count: state.count - 1 };
default:
return state;
}
};
export default counterReducer;
Selectors
Define typed selectors to access specific parts of the state
import { RootState } from './store';
export const selectCount = (state: RootState) => state.counter.count;
Store Setup
Configure the Redux store with typed state and middleware
import { createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer from './reducers';
const store = createStore(rootReducer, composeWithDevTools());
export default store;
export type RootState = ReturnType<typeof rootReducer>;
mobX with typeScript
Typed Actions
Define action types as TypeScript enums or string constants
import { action } from 'mobx';
enum ActionTypes {
INCREMENT = 'INCREMENT',
DECREMENT = 'DECREMENT',
}
const increment = action(ActionTypes.INCREMENT);
const decrement = action(ActionTypes.DECREMENT);
State and Stores
Define typed stores and observable state:
import { observable, action } from 'mobx';
class CounterStore {
@observable count = 0;
@action
increment() {
this.count++;
}
@action
decrement() {
this.count--;
}
}
const counterStore = new CounterStore();
Selectors
MobX doesn’t require explicit selectors as computed properties can be used directly within components to derive values from the store state.
Store Setup
MobX doesn’t require a centralized store setup like Redux. Instead you can create store instances and inject them where needed in your component tree.
By using TypeScript with Redux or MobX. you can make sure type safety throughout your state management codebase. From defining actions and reducers to accessing state values in components. This leads to more predictable and maintainable code.
Type-safe Routing with TypeScript
Handling routing in a type-safe manner is essential for maintaining code integrity and preventing runtime errors. Libraries like React Router provide robust solutions for routing in React applications and when combined with TypeScript. You can make sure type safety throughout your routing setup. Let’s explore how to handle routing in a type-safe manner with React Router and TypeScript, covering route parameters, query parameters and route guarding
Defining Routes
Define routes using React Router’s component. You can use the path prop to specify the route path and the component prop to specify the component to render for that route.
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom';
import Home from './Home';
import About from './About';
const App: React.FC = () => {
return (
<Router>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
</Switch>
</Router>
);
};
export default App;
Route Parameters
Use route parameters to handle dynamic segments of the URL. You can define route parameters by specifying a colon followed by the parameter name in the route path.
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom';
import UserDetails from './UserDetails';
const App: React.FC = () => {
return (
<Router>
<Switch>
<Route path="/users/:userId" component={UserDetails} />
</Switch>
</Router>
);
};
export default App;
In the UserDetails component now you can access the userId parameter using React Router’s useParams hook or match.params.
Query Parameters
React Router doesn’t natively support query parameters. but you can use libraries like react-router-query-params to handle query parameters in a type-safe manner.
import { useQueryParams } from 'react-router-query-params';
const UserDetails: React.FC = () => {
const { userId } = useParams<{ userId: string }>();
const { queryParams } = useQueryParams();
// Access query parameters
const search = queryParams.search ?? '';
return (
<div>
<h2>User Details</h2>
<p>User ID: {userId}</p>
<p>Search Query: {search}</p>
</div>
);
};
Route Guarding
Route guarding involves preventing unauthorized access to certain routes based on user authentication or other criteria. You can implement route guarding using higher-order components (HOCs) or custom hooks.
import { Route, Redirect } from 'react-router-dom';
const PrivateRoute: React.FC<PrivateRouteProps> = ({ component: Component, isAuthenticated, ...rest }) => (
<Route
{...rest}
render={(props) =>
isAuthenticated ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: '/login', state: { from: props.location } }} />
)
}
/>
);
export default PrivateRoute;
In this example PrivateRoute is a custom route component that redirects users to the login page if they are not authenticated.
Advanced TypeScript Patterns in React
Advanced TypeScript patterns such as conditional types, mapped types, type utilities and utility types can greatly increase code readability, maintainability and type safety in React development. Let’s explore how these patterns can be applied in React:
Conditional Types in react typeScript
Conditional types allow you to create types that depend on a condition. In React development conditional types can be usefull for creating flexible and reusable components that handle different scenarios based on prop types.
type Props<T> = T extends 'button' ? ButtonProps : InputProps;
const MyComponent: React.FC<Props> = ({ type, ...props }) => {
return type === 'button' ? <Button {...props} /> : <Input {...props} />;
};
In this example the Props type depends on the value of the type prop. If type is ‘button’ Props will be inferred as ButtonProps. Otherwise it will be inferred as InputProps.
Mapped Types in react typeScript
Mapped types allow you to transform the properties of an existing type. In React development mapped types can be used to add or modify props of a component dynamically based on certain conditions.
type NullableProps<T> = {
[P in keyof T]: T[P] | null;
};
const MyComponent: React.FC<NullableProps<Props>> = ({ data }) => {
// 'data' prop can now be null or undefined
return <div>{data}</div>;
};
Here NullableProps transforms each property in Props to be nullable that allowing for more flexible handling of props in components.
Type Utilities in react typeScript
TypeScript provides several built-in type utilities that can be used to manipulate types. Common type utilities like Partial, Required, Pick and Omit can be particularly usefull in React development for working with props and state.
interface User {
id: number;
name: string;
email: string;
}
const updateUser: Partial<User> = { name: 'John' };
Here Partial<user>
is used to create a type where all properties of User are optional that allowing for partial updates to user data.
Utility Types in react typeScript
Utility types are predefined generic types provided by TypeScript. They can be used to perform common type transformations without having to manually define them. In React development utility types like ReturnType, Parameters and InstanceType can be valuable for working with functions, components and hooks.
type ReturnTypes = ReturnType<typeof someFunction>;
Here ReturnType is used to extract the return type of the someFunction function that allowing for type-safe manipulation of its return value.
Best Practices and Tips for TypeScript in React Development
Using TypeScript effectively in React development requires following best practices, focus to naming conventions and utilizing available resources. Here are some tips to help you make the most out of TypeScript in your React projects
- Code Organization :
- Keep your code well-organized by grouping related components, utilities and types into separate directories.
- Utilize features like folders, subfolders and index files to maintain a clear project structure.
- Separate concerns by following the principles of component-based architecture.
- Naming Conventions :
- Follow consistent naming conventions for variables, functions, components and types.
- Use descriptive names that convey the purpose and functionality of each entity
- Consider adopting popular naming conventions like camelCase or PascalCase for consistency.
- Type Safety :
- Use TypeScript’s static typing to catch errors early in the development process.
- Use interfaces and types to define the shape of objects, props and state.
- Avoid using the “any” type whenever possible to maintain strong type safety throughout your codebase.
- Usefull TypeScript Resources :
- Official TypeScript Documentation: The official documentation provides comprehensive guides, tutorials and references for learning TypeScript.
- TypeScript GitHub Repository: Explore the TypeScript repository on GitHub for source code, issues and discussions related to the TypeScript language.
- Linting and Formatting :
- Configure ESLint and Prettier to push coding standards and formatting rules in your TypeScript code.
- Utilize plugins and presets specifically made for TypeScript to make sure consistent code style and quality.
- Testing :
- Write comprehensive unit tests and integration tests for your React components and TypeScript utilities.
- Use testing frameworks like Jest and testing libraries like React Testing Library to automate testing and code reliability.
conclusion
Wrapping React with TypeScript is like giving your web development superpowers. TypeScript adds some cool features to JavaScript that making it easier for developers to catch mistakes early and write cleaner code.Think of React as your go-to library for building stylish user interfaces on the web. Now when you team it up with TypeScript it’s like adding an extra layer of safety and organization to your code.
With TypeScript now you can define what type of data your variables, functions and React components should expect. This helps prevent silly errors and makes your code easier to understand for you and your team.TypeScript plays really nicely with React. You can use it to define the props and state of your React components that making them more reliable and easier to work with.
Setting up TypeScript with Create React App is so easy. Just a few commands and you are ready to start building your awesome web app with all the benefits of TypeScript.If you ever get stuck there are plenty of resources out there to help you out. The TypeScript documentation is super helpfull and there are tons of tutorials and communities where you can get support.
FAQs
Why should I use TypeScript with React ?
TypeScript brings strong typing to JavaScript, helping catch errors early in the development process and improving code readability and maintainability. React used with TypeScript allows you to define the types of props and state for your components that making them more reliable and easier to work with.
How does TypeScript improve code quality in React applications ?
TypeScript adds static typing to JavaScript which helps catch errors at compile time rather than runtime. By defining types for variables, props and state. TypeScript make sure that your code is more predictable, easier to understand and less chance of bugs. Additionally TypeScript provides better tooling support such as code completion and type checking which further increase code quality and developer productivity.