useState с типами
Простые типы
const [count, setCount] = useState(0);
// number
const [name, setName] = useState('');
// string
const [active, setActive] = useState(false);
// boolean
TypeScript выводит тип из начального значения.
Union
const [status, setStatus] = useState<'idle' | 'loading' | 'success'>('idle');
Или через as const:
const [status, setStatus] = useState('idle' as 'idle' | 'loading' | 'success');
Object
type User = {
name: string;
age: number;
};
const [user, setUser] = useState<User | null>(null);
null в начальном значении требует union.
Массив
const [items, setItems] = useState<string[]>([]);
Без явного типа TypeScript выведет never[] для пустого массива.
const [items, setItems] = useState<string[]>([]); // правильно
const [items, setItems] = useState([]); // never[]
Массив объектов
type Post = { id: number; title: string };
const [posts, setPosts] = useState<Post[]>([]);
Пустой объект
const [data, setData] = useState<Record<string, unknown>>({});
Ленивая инициализация
const [data, setData] = useState<User | null>(() => {
const stored = localStorage.getItem('user');
return stored ? JSON.parse(stored) : null;
});
Функциональное обновление
setCount((prev) => prev + 1);
Тип prev выводится из count.
Обновление объекта
setUser((prev) => (prev ? { ...prev, name: 'Боб' } : null));
Обновление массива
setItems((prev) => [...prev, 'новый']);
setItems((prev) => prev.filter((i) => i !== 'удалить'));
Несколько состояний
const [name, setName] = useState('');
const [email, setEmail] = useState('');
Или одно:
type FormState = {
name: string;
email: string;
};
const [form, setForm] = useState<FormState>({ name: '', email: '' });
Дискриминатор
type State =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: User }
| { status: 'error'; error: Error };
const [state, setState] = useState<State>({ status: 'idle' });
При обновлении проверяется, что поля соответствуют статусу:
setState({ status: 'success', data: user }); // ок
setState({ status: 'success' }); // ошибка
Обновление через reducer
setState((prev) => {
if (prev.status === 'loading') {
return { status: 'success', data: user };
}
return prev;
});
Generic-хук
function useToggle(initial: boolean): [boolean, () => void] {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue((v) => !v), []);
return [value, toggle];
}
С дженериком:
function useArray<T>(initial: T[] = []): [T[], (item: T) => void] {
const [items, setItems] = useState<T[]>(initial);
const add = useCallback((item: T) => setItems((prev) => [...prev, item]), []);
return [items, add];
}
const [users, addUser] = useArray<User>();
Итоги
- Простые типы выводятся
- Пустой массив — явный тип
null— через unionas constдля литералов- Дискриминатор — для сложных состояний
- Generic для переиспользуемых хуков