Skip to main content

Forms and Validation in TypeScript

Level: Beginner to Intermediate

ℹ️ What You'll Learn
  • What Forms and Validation 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

Forms and Validation 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 catches form errors before runtime.

The Problem

React JavaScript can fail at runtime when props, API responses, or form values have the wrong shape. This lesson shows how Forms and Validation in TypeScript uses TypeScript to catch many of those mistakes while you write code, before the student dashboard reaches users.

Typed Form State

interface StudentFormData {
name: string;
email: string;
className: string;
parentName: string;
parentPhone: string;
}

type FormErrors = Partial<Record<keyof StudentFormData, string>>;

function StudentRegistration() {
const [form, setForm] = useState<StudentFormData>({
name: '',
email: '',
className: '10',
parentName: '',
parentPhone: ''
});

const [errors, setErrors] = useState<FormErrors>({});

const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
const { name, value } = e.target;
setForm(prev => ({
...prev,
[name]: value
}));
};

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// validate and submit
};

return <form onSubmit={handleSubmit}>{/* */}</form>;
}

Validation Types

type ValidationRule<T> = (value: T) => string | null;

interface Validators {
[K in keyof StudentFormData]: ValidationRule<StudentFormData[K]>;
}

const validators: Validators = {
name: (value: string) => {
if (!value.trim()) return 'Name required';
if (value.length < 2) return 'Name too short';
return null;
},
email: (value: string) => {
if (!value.includes('@')) return 'Valid email required';
return null;
},
className: () => null,
parentName: () => null,
parentPhone: (value: string) => {
if (value && value.length < 10) return 'Phone must be 10+ digits';
return null;
}
};

function validateForm(form: StudentFormData): FormErrors {
const errors: FormErrors = {};

(Object.keys(form) as Array<keyof StudentFormData>).forEach(field => {
const error = validators[field](form[field]);
if (error) errors[field] = error;
});

return errors;
}

Form Submission Type

interface SubmitHandler<T> {
(data: T): Promise<void>;
}

const handleSubmit: SubmitHandler<StudentFormData> = async (data) => {
const response = await fetch('/api/students', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});

if (!response.ok) throw new Error('Failed to submit');
};

Controlled Input Component

interface TextInputProps {
name: keyof StudentFormData;
value: string;
error?: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
placeholder?: string;
}

function TextInput({ name, value, error, onChange, placeholder }: TextInputProps) {
return (
<div>
<input
name={name}
value={value}
onChange={onChange}
placeholder={placeholder}
/>
{error && <p style={{ color: 'red' }}>{error}</p>}
</div>
);
}

// Usage
<TextInput
name="name"
value={form.name}
error={errors.name}
onChange={handleChange}
placeholder="Name"
/>

Custom Hook for Forms

function useForm<T>(
initialValues: T,
onSubmit: (values: T) => Promise<void>,
validate?: (values: T) => Partial<Record<keyof T, string>>
) {
const [values, setValues] = useState<T>(initialValues);
const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({});
const [isSubmitting, setIsSubmitting] = useState(false);

const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
const { name, value } = e.target;
setValues(prev => ({
...prev,
[name as keyof T]: value
}));
};

const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

if (validate) {
const newErrors = validate(values);
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
}

setIsSubmitting(true);
try {
await onSubmit(values);
setValues(initialValues);
} finally {
setIsSubmitting(false);
}
};

return { values, errors, handleChange, handleSubmit, isSubmitting };
}

// Usage
function StudentForm() {
const { values, errors, handleChange, handleSubmit, isSubmitting } =
useForm<StudentFormData>(
{
name: '',
email: '',
className: '10',
parentName: '',
parentPhone: ''
},
async (data) => {
await fetch('/api/students', {
method: 'POST',
body: JSON.stringify(data)
});
},
(values) => {
const errors: FormErrors = {};
if (!values.name) errors.name = 'Required';
return errors;
}
);

return (
<form onSubmit={handleSubmit}>
<input name="name" value={values.name} onChange={handleChange} />
{errors.name && <p>{errors.name}</p>}
<button type="submit" disabled={isSubmitting}>Submit</button>
</form>
);
}

Key Takeaways

  • Type form state with interfaces
  • Use Partial for error state
  • Validate with typed functions
  • Create custom hooks for forms
  • Generic hooks for reusability
  • Next: Styling in TypeScript
⚠️ Form Type Mistakes
  1. Not typing form state — Easy to mess up
  2. Any error types — Lose validation
  3. Unsafe field names — Use keyof for safety
  4. Not handling async — Forget to await
💡 Safe Field Access
// ❌ Unsafe — typo possible
[name]: value

// ✓ Safe — TypeScript checks field names
[name as keyof StudentFormData]: value
🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on Typed Forms. Try these prompts:

  • "How do you type form state and errors?"
  • "What's Partial<Record<keyof T, string>>?"
  • "How do you create a reusable form hook?"
  • "Quiz me on TypeScript forms"

💡 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

  • Forms and Validation 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 any too 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 Forms and Validation in TypeScript. Keep it connected to a School Management System scenario.

Suggested practice:

  1. Define a clear Student, Teacher, or Attendance type.
  2. Build a small typed component or hook.
  3. Add one valid example and one intentionally wrong example to see TypeScript errors.
  4. Explain the type contract in your own words.
  5. Rebuild the same example once without looking at the article.

Quick Revision

QuestionAnswer
What is the main idea?Use TypeScript to make Forms and Validation 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.
🎯 How would you explain Forms and Validation in TypeScript in an interview?

Forms and Validation 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.

🎯 Where is this used in a real React TypeScript project?

It is used in typed props, API response models, form state, route parameters, context values, custom hooks, and reusable UI components.

Next Article

Conditional Rendering in TypeScript ->

nexcoding.in