Overview
TypeScript adds static typing to JavaScript, helping you catch errors early and improve code quality. This guide covers using TypeScript with React components.
TypeScript provides autocomplete, type checking, and better refactoring support in your React projects.
TypeScript Basics
Primitive Types
TypeScript includes basic types for JavaScript primitives:
// Primitives
let age : number ;
age = 12 ;
let userName : string ;
userName = 'Max' ;
let isInstructor : boolean ;
isInstructor = true ;
Arrays and Objects
// Array of strings
let hobbies : string [];
hobbies = [ 'Sports' , 'Cooking' ];
// Object type
let person : {
name : string ;
age : number ;
};
person = {
name: 'Max' ,
age: 32
};
// Array of objects
let people : {
name : string ;
age : number ;
}[];
TypeScript can often infer types automatically, so you don’t always need to specify them explicitly.
Union Types
Union types allow a variable to hold multiple types:
let userName : string | string [];
userName = 'Max' ; // OK
userName = [ 'Max' , 'Anna' ]; // Also OK
let course : string | number = 'React - The Complete Guide' ;
course = 12341 ; // OK
Flexibility Union types provide flexibility while maintaining type safety.
Type Guards TypeScript can narrow types based on runtime checks.
Type Aliases
Create reusable type definitions with type aliases:
type Person = {
name : string ;
age : number ;
};
let person : Person ;
person = {
name: 'Max' ,
age: 32
};
let people : Person [];
Type aliases improve code readability and make complex types easier to maintain.
Generics
Generics enable type-safe reusable functions:
function insertAtBeginning < T >( array : T [], value : T ) {
const newArray = [ value , ... array ];
return newArray ;
}
const demoArray = [ 1 , 2 , 3 ];
const updatedArray = insertAtBeginning ( demoArray , - 1 ); // [-1, 1, 2, 3]
const stringArray = insertAtBeginning ([ 'a' , 'b' , 'c' ], 'd' );
// TypeScript knows this is string[]
Generics provide:
Type safety without sacrificing flexibility
Better autocomplete and IntelliSense
Compile-time error detection
Reusable, type-safe functions
Use generics when:
Creating reusable utility functions
Working with arrays or collections
Building components that work with various data types
Defining function or component props that vary
React Components with TypeScript
Typing Props
Define prop types using TypeScript interfaces or inline types:
Simple Props
Function Props
import React from 'react' ;
const Todos : React . FC <{ items : string [] }> = ( props ) => {
return (
< ul >
{ props . items . map (( item ) => (
< li key = { item } > { item } </ li >
)) }
</ ul >
);
};
export default Todos ;
Define the component type
Use React.FC<PropsType> to define a functional component with typed props.
Specify prop types
Provide an object type or interface describing the component’s props.
Access props with autocomplete
TypeScript provides IntelliSense for all prop names and types.
Using Custom Classes
Create model classes with TypeScript:
Model Class
Using the Model
class Todo {
id : string ;
text : string ;
constructor ( todoText : string ) {
this . text = todoText ;
this . id = new Date (). toISOString ();
}
}
export default Todo ;
Working with Refs
Type refs using generic parameters:
import { useRef } from 'react' ;
const NewTodo : React . FC = () => {
const todoTextInputRef = useRef < HTMLInputElement >( null );
const submitHandler = ( event : React . FormEvent ) => {
event . preventDefault ();
// Non-null assertion operator (!)
const enteredText = todoTextInputRef . current ! . value ;
if ( enteredText . trim (). length === 0 ) {
return ;
}
};
return (
< form onSubmit = { submitHandler } >
< input type = 'text' ref = { todoTextInputRef } />
< button > Add Todo </ button >
</ form >
);
};
The ! operator tells TypeScript that the value will definitely exist. Use carefully to avoid runtime errors.
Typing State
Use generics to type useState:
import { useState } from 'react' ;
import Todo from './models/todo' ;
function App () {
const [ todos , setTodos ] = useState < Todo []>([]);
const addTodoHandler = ( todoText : string ) => {
const newTodo = new Todo ( todoText );
setTodos (( prevTodos ) => {
return prevTodos . concat ( newTodo );
});
};
return (
< div >
< NewTodo onAddTodo = { addTodoHandler } />
< Todos items = { todos } />
</ div >
);
}
Type Inference TypeScript can often infer state types from initial values.
Generic Types Use generics for complex state like arrays of objects.
Context API with TypeScript
Type Context using interfaces:
import React , { useState } from 'react' ;
import Todo from '../models/todo' ;
type TodosContextObj = {
items : Todo [];
addTodo : ( text : string ) => void ;
removeTodo : ( id : string ) => void ;
};
export const TodosContext = React . createContext < TodosContextObj >({
items: [],
addTodo : () => {},
removeTodo : ( id : string ) => {},
});
const TodosContextProvider : React . FC <{ children : React . ReactNode }> = ( props ) => {
const [ todos , setTodos ] = useState < Todo []>([]);
const addTodoHandler = ( todoText : string ) => {
const newTodo = new Todo ( todoText );
setTodos (( prevTodos ) => prevTodos . concat ( newTodo ));
};
const removeTodoHandler = ( todoId : string ) => {
setTodos (( prevTodos ) => prevTodos . filter ( todo => todo . id !== todoId ));
};
const contextValue : TodosContextObj = {
items: todos ,
addTodo: addTodoHandler ,
removeTodo: removeTodoHandler ,
};
return (
< TodosContext.Provider value = { contextValue } >
{ props . children }
</ TodosContext.Provider >
);
};
export default TodosContextProvider ;
Define context type
Create a type describing the context value shape.
Create typed context
Pass the type as a generic parameter to createContext().
Use in components
TypeScript now provides autocomplete for context values.
Event Types
Common React event types in TypeScript:
Form Events const handleSubmit = (
event : React . FormEvent
) => {
event . preventDefault ();
};
Click Events const handleClick = (
event : React . MouseEvent
) => {
console . log ( 'Clicked!' );
};
Change Events const handleChange = (
event : React . ChangeEvent < HTMLInputElement >
) => {
setValue ( event . target . value );
};
Keyboard Events const handleKeyPress = (
event : React . KeyboardEvent
) => {
if ( event . key === 'Enter' ) {
submit ();
}
};
Best Practices
Use type inference when possible
Let TypeScript infer types from initial values: // Type is inferred as number
const count = 0 ;
// Explicit type needed for empty arrays
const items : string [] = [];
Prefer interfaces for objects
Use interfaces for object shapes, especially for props: interface TodoProps {
id : string ;
text : string ;
onRemove : ( id : string ) => void ;
}
const Todo : React . FC < TodoProps > = ( props ) => { /* ... */ };
Use strict TypeScript settings in tsconfig.json: {
"compilerOptions" : {
"strict" : true ,
"noImplicitAny" : true ,
"strictNullChecks" : true
}
}
Instead of any, use:
unknown for truly unknown types
Generics for flexible but type-safe code
Union types for multiple specific types
// Bad
function process ( data : any ) { }
// Good
function process < T >( data : T ) { }
Common Patterns
Props with Children
Optional Props
Default Props
interface ContainerProps {
children : React . ReactNode ;
title : string ;
}
const Container : React . FC < ContainerProps > = ({ children , title }) => {
return (
< div >
< h2 > { title } </ h2 >
{ children }
</ div >
);
};
interface ButtonProps {
label : string ;
onClick : () => void ;
disabled ?: boolean ; // Optional
}
const Button : React . FC < ButtonProps > = ({
label ,
onClick ,
disabled = false
}) => {
return (
< button onClick = { onClick } disabled = { disabled } >
{ label }
</ button >
);
};
interface CardProps {
title : string ;
variant ?: 'primary' | 'secondary' ;
}
const Card : React . FC < CardProps > = ({
title ,
variant = 'primary'
}) => {
return < div className = { variant } > { title } </ div > ;
};
TypeScript + React Cheat Sheet
Component Props
State
Refs
Events
interface MyComponentProps {
name : string ;
age : number ;
onSave : ( name : string ) => void ;
}
const MyComponent : React . FC < MyComponentProps > = ( props ) => {
// Component implementation
};
Next Steps
Testing Learn how to test React components with TypeScript
Advanced Types Explore utility types, conditional types, and mapped types