State and useState in TypeScript
Level: Beginner to Intermediate
- What State and useState in TypeScript means in React + TypeScript
- How type safety improves React components
- How to model School Management System data with interfaces and types
- Common TypeScript mistakes to avoid
- How to explain this topic in interviews
Why This Matters
State and useState in TypeScript helps you build React screens with fewer runtime bugs. In real .NET Web API projects, TypeScript makes API DTOs, component props, form state, and shared data contracts easier to understand and safer to change.
TypeScript makes state safe by requiring type declarations.
The Problem
React JavaScript can fail at runtime when props, API responses, or form values have the wrong shape. This lesson shows how State and useState in TypeScript uses TypeScript to catch many of those mistakes while you write code, before the student dashboard reaches users.
Basic useState Typing
// Explicit type
const [count, setCount] = useState<number>(0);
// Type inference (TypeScript guesses type)
const [name, setName] = useState('Ravi'); // inferred as string
// Union type for optional
const [student, setStudent] = useState<Student | null>(null);
// Array of objects
const [students, setStudents] = useState<Student[]>([]);
// Object state
interface FormData {
name: string;
email: string;
className: string;
}
const [form, setForm] = useState<FormData>({
name: '',
email: '',
className: ''
});
Complex State Patterns
Async State with Loading/Error
interface AsyncState<T> {
data: T | null;
loading: boolean;
error: Error | null;
}
function StudentDetail({ studentId }: { studentId: number }) {
const [state, setState] = useState<AsyncState<Student>>({
data: null,
loading: true,
error: null
});
useEffect(() => {
fetch(`/api/students/${studentId}`)
.then(r => r.json())
.then(data => setState({ data, loading: false, error: null }))
.catch(error => setState({ data: null, loading: false, error }));
}, [studentId]);
if (state.loading) return <p>Loading...</p>;
if (state.error) return <p>Error: {state.error.message}</p>;
return <div>{state.data?.name}</div>;
}
Union State
type StudentState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'error'; error: Error }
| { status: 'success'; data: Student };
function StudentDetail() {
const [state, setState] = useState<StudentState>({ status: 'idle' });
if (state.status === 'loading') return <p>Loading...</p>;
if (state.status === 'error') return <p>{state.error.message}</p>;
if (state.status === 'success') return <p>{state.data.name}</p>;
return null;
}
Updater Functions
const [count, setCount] = useState<number>(0);
// Updater function style (safer)
setCount((prev: number) => prev + 1);
// Type inference works
setCount(prev => prev + 1); // TypeScript infers prev is number
Form State Typing
interface StudentForm {
name: string;
email: string;
className: string;
marks: number;
isActive: boolean;
}
function StudentForm() {
const [form, setForm] = useState<StudentForm>({
name: '',
email: '',
className: '10',
marks: 0,
isActive: true
});
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
const { name, value, type } = e.target;
setForm(prev => ({
...prev,
[name]: type === 'number' ? parseFloat(value) : value
}));
};
return (
<form>
<input name="name" value={form.name} onChange={handleChange} />
<input name="marks" type="number" value={form.marks} onChange={handleChange} />
</form>
);
}
Enum State
enum StudentStatus {
Active = 'Active',
Inactive = 'Inactive',
Graduated = 'Graduated'
}
function StudentFilter() {
const [status, setStatus] = useState<StudentStatus>(StudentStatus.Active);
return (
<select value={status} onChange={e => setStatus(e.target.value as StudentStatus)}>
<option value={StudentStatus.Active}>Active</option>
<option value={StudentStatus.Inactive}>Inactive</option>
<option value={StudentStatus.Graduated}>Graduated</option>
</select>
);
}
Discriminated Union State
type ViewState =
| { view: 'list'; students: Student[] }
| { view: 'detail'; student: Student }
| { view: 'error'; message: string };
function StudentView() {
const [state, setState] = useState<ViewState>({ view: 'list', students: [] });
if (state.view === 'list') {
return <ul>{state.students.map(s => <li key={s.id}>{s.name}</li>)}</ul>;
}
if (state.view === 'detail') {
return <div>{state.student.name}</div>;
}
if (state.view === 'error') {
return <p>{state.message}</p>;
}
}
Key Takeaways
- Use
useState<T>()for type safety - Type inference reduces boilerplate
- Complex states need interfaces
- Union types for different states
- Discriminated unions for clarity
- Next: useEffect with types
- Not typing state — Lose type checking
- Using
any— Defeats TypeScript - Wrong type on updater — State becomes wrong type
- Forgetting union — State can be multiple types
TypeScript often infers types, so you don't always need explicit types:
// TypeScript infers type from initial value
const [count, setCount] = useState(0); // inferred: number
const [active, setActive] = useState(true); // inferred: boolean
const [students, setStudents] = useState<Student[]>([]); // explicit (empty array can't infer)
Use ChatGPT, Claude, or Copilot to go deeper on Typed State. Try these prompts:
"Why type useState state?""What's the difference between useState<T | null> and optional?""When would you use discriminated unions?""Quiz me on TypeScript state"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.
Quick Definitions
- State and useState in TypeScript - The main React + TypeScript concept explained in this lesson.
- Type/interface - A contract that describes the shape of data.
- Typed props/state - React data with clear compile-time expectations.
- API DTO - The request or response shape shared with ASP.NET Core Web API.
Common Mistakes
- Using
anytoo quickly instead of defining a useful type - Typing props loosely and losing the benefit of TypeScript
- Forgetting that API data can still be missing or invalid at runtime
- Making types too complex before the component is stable
- Not sharing clear DTO shapes between frontend and backend teams
Practice Task
Create a small React TypeScript example using State and useState in TypeScript. Keep it connected to a School Management System scenario.
Suggested practice:
- Define a clear
Student,Teacher, orAttendancetype. - Build a small typed component or hook.
- Add one valid example and one intentionally wrong example to see TypeScript errors.
- Explain the type contract in your own words.
- Rebuild the same example once without looking at the article.
Quick Revision
| Question | Answer |
|---|---|
| What is the main idea? | Use TypeScript to make State and useState in TypeScript safer in React. |
| Where is it used? | Props, state, forms, API responses, context, hooks, and routes. |
| What should beginners avoid? | Overusing any and ignoring runtime API validation. |
| What is the best debugging habit? | Read the TypeScript error, check the data shape, and fix the type contract. |
State and useState in TypeScript is a React + TypeScript concept that improves safety and maintainability. I would explain which values need types, how TypeScript catches mistakes early, and how it helps when consuming ASP.NET Core Web API responses.
It is used in typed props, API response models, form state, route parameters, context values, custom hooks, and reusable UI components.