Зачем TypeScript в React
▸Преимущества
TypeScript ловит ошибки на этапе компиляции, обеспечивает автодополнение, документирует интерфейсы и делает рефакторинг безопаснее.
Настройка проекта
▸Новый проект
1npx create-react-app my-app --template typescript2# или с Vite3npm create vite@latest my-app -- --template react-ts
▸tsconfig.json
1{2 "compilerOptions": {3 "target": "ES2020",4 "lib": ["ES2020", "DOM", "DOM.Iterable"],5 "module": "ESNext",6 "strict": true,7 "jsx": "react-jsx",8 "moduleResolution": "bundler",9 "allowImportingTsExtensions": true,10 "noEmit": true,11 "paths": {12 "@/*": ["./src/*"]13 }14 }15}
Типизация пропсов
▸Interface для пропсов
1interface ButtonProps {2 children: React.ReactNode;3 variant?: 'primary' | 'secondary' | 'danger';4 size?: 'sm' | 'md' | 'lg';5 onClick?: () => void;6 disabled?: boolean;7}89function Button({ children, variant = 'primary', size = 'md', onClick, disabled }: ButtonProps) {10 return (11 <button12 className={`btn btn-${variant} btn-${size}`}13 onClick={onClick}14 disabled={disabled}15 >16 {children}17 </button>18 );19}
▸extends React.HTMLAttributes
1interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {2 label: string;3 error?: string;4}56function Input({ label, error, ...props }: InputProps) {7 return (8 <div>9 <label>{label}</label>10 <input {...props} />11 {error && <span className="error">{error}</span>}12 </div>13 );14}
Типизация children
▸React.ReactNode
1interface CardProps {2 title: string;3 children: React.ReactNode;4}56function Card({ title, children }: CardProps) {7 return (8 <div className="card">9 <h3>{title}</h3>10 {children}11 </div>12 );13}
▸React.FC (не рекомендуется)
1// Не рекомендуется — проблемы с generics и children2const Button: React.FC<ButtonProps> = ({ children }) => {3 return <button>{children}</button>;4};
Типизация хуков
▸useState
1// Автовывод типа2const [count, setCount] = useState(0);34// Явный тип5const [user, setUser] = useState<User | null>(null);67// Типизация объекта8const [form, setForm] = useState<{9 email: string;10 password: string;11}>({ email: '', password: '' });
▸useRef
1// DOM ref2const inputRef = useRef<HTMLInputElement>(null);34// Mutable ref5const timerRef = useRef<number | null>(null);67// Использование8inputRef.current?.focus();
▸custom hooks
1function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T) => void] {2 const [storedValue, setStoredValue] = useState<T>(() => {3 try {4 const item = window.localStorage.getItem(key);5 return item ? JSON.parse(item) : initialValue;6 } catch {7 return initialValue;8 }9 });1011 const setValue = (value: T) => {12 setStoredValue(value);13 window.localStorage.setItem(key, JSON.stringify(value));14 };1516 return [storedValue, setValue];17}
Типизация событий
▸События форм
1function Form() {2 const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {3 e.preventDefault();4 const formData = new FormData(e.currentTarget);5 console.log(formData.get('email'));6 };78 return (9 <form onSubmit={handleSubmit}>10 <input type="email" name="email" />11 <button type="submit">Отправить</button>12 </form>13 );14}
▸Кастомные события
1interface SelectProps {2 options: string[];3 onSelect: (value: string) => void;4}56function Select({ options, onSelect }: SelectProps) {7 return (8 <select onChange={(e) => onSelect(e.target.value)}>9 {options.map(opt => (10 <option key={opt} value={opt}>{opt}</option>11 ))}12 </select>13 );14}
Дженерики в компонентах
1interface ListProps<T> {2 items: T[];3 renderItem: (item: T) => React.ReactNode;4 keyExtractor: (item: T) => string;5}67function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {8 return (9 <ul>10 {items.map(item => (11 <li key={keyExtractor(item)}>{renderItem(item)}</li>12 ))}13 </ul>14 );15}1617// Использование18<List19 items={users}20 renderItem={(user) => <span>{user.name}</span>}21 keyExtractor={(user) => user.id}22/>
Утилиты типов
▸Extract и Exclude
1type ButtonVariant = 'primary' | 'secondary' | 'danger';2type PrimaryVariant = Extract<ButtonVariant, 'primary'>; // 'primary'3type NonPrimary = Exclude<ButtonVariant, 'primary'>; // 'secondary' | 'danger'
▸Partial, Required, Pick, Omit
1interface User {2 id: string;3 name: string;4 email: string;5 avatar?: string;6}78type UserUpdate = Partial<User>; // все поля опциональны9type UserCreate = Required<User>; // все поля обязательны10type UserPreview = Pick<User, 'id' | 'name'>; // только id и name11type UserWithoutAvatar = Omit<User, 'avatar'>; // без avatar
Заключение
TypeScript делает React-приложения надежнее. Используйте interface для пропсов, generics для переиспользуемых компонентов и утилиты типов для сокращения boilerplate. Strict mode в tsconfig — must-have для серьезных проектов.