TypeScript with React
(Updated on )
TypeScript with React
TypeScript and React make a powerful combination for building robust web applications. In this guide, we’ll explore how to use TypeScript effectively with React.
Setting Up TypeScript with React
Installation
npx create-react-app my-app --template typescript
Basic Configuration
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
}
}
Typing React Components
Functional Components
interface ButtonProps {
text: string;
onClick: () => void;
disabled?: boolean;
}
const Button: React.FC<ButtonProps> = ({ text, onClick, disabled }) => {
return (
<button onClick={onClick} disabled={disabled}>
{text}
</button>
);
};
Class Components
interface CounterProps {
initialCount: number;
}
interface CounterState {
count: number;
}
class Counter extends React.Component<CounterProps, CounterState> {
state: CounterState = {
count: this.props.initialCount
};
increment = () => {
this.setState(state => ({
count: state.count + 1
}));
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
Hooks with TypeScript
useState
const [count, setCount] = useState<number>(0);
const [user, setUser] = useState<User | null>(null);
useEffect
useEffect(() => {
const fetchData = async () => {
const response = await fetch('/api/data');
const data: ApiResponse = await response.json();
setData(data);
};
fetchData();
}, []);
useReducer
interface State {
count: number;
}
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'reset' };
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
return state;
}
};
Context with TypeScript
interface ThemeContextType {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
const ThemeContext = React.createContext<ThemeContextType | undefined>(undefined);
const ThemeProvider: React.FC = ({ children }) => {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
Custom Hooks
interface UseWindowSize {
width: number;
height: number;
}
function useWindowSize(): UseWindowSize {
const [size, setSize] = useState<UseWindowSize>({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
Best Practices
- Use proper type definitions for props and state
- Leverage TypeScript’s type inference
- Use interfaces for complex data structures
- Implement proper error handling
- Use type guards for runtime type checking
Next Steps
- Learn about TypeScript Basics
- Explore Advanced TypeScript
- Master TypeScript Design Patterns
Happy coding!