React with TypeScript
React with TypeScript
A student card expects a numeric mark, but another screen accidentally supplies text. The mistake may remain hidden until arithmetic produces an unexpected result. A written component contract can catch many such mistakes while editing.
TypeScript adds static type checking to JavaScript. Static checking examines source before execution. A type describes expected values; it does not automatically validate server responses or generate a form validator.
Describe props
Write a student record and callback contract.
Keep runtime behavior clear
Compare typed source with the equivalent JSX.
Validate boundaries
Distinguish source checks from checks of external data.
Follow information through the application
Type
The source describes ID, name and numeric mark.
Check
Tools compare callers with the component contract.
Runtime
JavaScript renders and handles selection.
A type is a source-level promise
A Student type gives names and types to the fields this component needs. StudentCardProps also describes onSelect: it accepts a string ID and returns no useful result, written void. Describing the callback prevents a caller from assuming it receives a full student record when the component actually sends an ID.
Use .tsx for TypeScript files containing JSX. The project needs a TypeScript configuration and appropriate React types. React’s TypeScript guide explains props, Hook inference and common setup requirements. Types help editors and checks, while React still uses the same component and state model.
Complete typed App.tsx in a configured project
This App.tsx includes the record shape, component prop contract and state owner. Save it in a React project configured for TypeScript; it is not ordinary JSX that a browser can execute unchanged. The initial screen shows Maya, Mark: 35 and Selected: None. Selecting the button changes the last label to Selected: 007.
import React, { useState } from 'react';
type Student = { id: string; name: string; mark: number };
type StudentCardProps = {
student: Student;
onSelect: (id: string) => void;
};
function StudentCard({ student, onSelect }: StudentCardProps) {
return <article><h2>{student.name}</h2><p>Mark: {student.mark}</p>
<button onClick={() => onSelect(student.id)}>Select student</button></article>;
}
export default function App() {
const [selected, setSelected] = useState('None');
const student: Student = { id: '007', name: 'Maya', mark: 35 };
return <main><StudentCard student={student} onSelect={setSelected} />
<p>Selected: {selected}</p></main>;
}Changing mark: 35 to mark: "35" conflicts with the declared numeric field. The type checker can report that source error. An unchecked response from a remote service remains a different problem, because type annotations are removed before the resulting JavaScript runs.
Equivalent runtime JSX
This runnable JavaScript version deliberately has the same behavior as the typed version. The fields and callback remain meaningful even without their annotations. Select student passes 007 to the parent’s setter and displays the selected identifier.
Removing types does not remove React state or alter the event sequence. This comparison helps separate a tool that checks source contracts from the runtime that renders the interface. The example does not fetch records or claim the incoming data has been validated.
React · JavaScript with JSX
import React from 'react';
function StudentCard({ student, onSelect }) {
return <article><h2>{student.name}</h2><p>Mark: {student.mark}</p>
<button onClick={() => onSelect(student.id)}>Select student</button></article>;
}
export default function App() {
const [selected, setSelected] = React.useState('None');
const student = { id: '007', name: 'Maya', mark: 35 };
return <main><StudentCard student={student} onSelect={setSelected} />
<p>Selected: {selected}</p></main>;
}Expected screen and behavior
Initially: Maya, Mark: 35, Select student and Selected: None.
Selecting changes the label to Selected: 007.Infer the obvious, describe the boundary
useState("None") lets TypeScript infer a string state. A more precise selection design might use string | null, meaning either a student ID or no selected student. The rendering code then explicitly handles null. Avoid using the word None as a real ID if school data could contain that value.
Annotate component props and external function boundaries where the contract matters. Repeating a type on every local variable can make code noisy without adding information. Event types can be inferred for inline handlers, while extracted handlers may need an explicit React event type appropriate to the element.
Worked practice: represent no selection with null
Replace the placeholder text with a null state and show No student selected until the button is used. The complete runtime answer uses a conditional display. In a typed project, its corresponding state declaration is useState<string | null>(null).
After selection, the same string ID appears. This represents an absent choice directly rather than requiring every reader to know that a particular string is a sentinel. The prop callback still accepts one ID, so its contract does not need to change.
React · JavaScript with JSX
import React from 'react';
function StudentCard({ student, onSelect }) {
return <article><h2>{student.name}</h2><p>Mark: {student.mark}</p>
<button onClick={() => onSelect(student.id)}>Select student</button></article>;
}
export default function App() {
const [selected, setSelected] = React.useState(null);
const student = { id: '007', name: 'Maya', mark: 35 };
return <main><StudentCard student={student} onSelect={setSelected} />
<p>{selected === null ? 'No student selected' : 'Selected: ' + selected}</p></main>;
}Expected screen and behavior
Initially: No student selected.
Selecting displays Selected: 007.A type assertion is not a data check
Writing a type assertion such as response as Student tells the checker to trust you; it does not inspect fields at runtime. Parse external data as unknown and verify the required fields, or use a deliberate validation library. In particular, a numeric-looking string is still text until your application applies its chosen conversion policy.
Many development tools transpile TypeScript quickly without performing the full type check. Run the project’s actual type-check command as well as its production build. Types cannot prove server authorization, correct school grading rules or accessible markup; those still need requirements and appropriate verification.
Check the callback contract
Apply the external-data rule
Use types to communicate, checks to validate
Describe records and component callbacks, let inference handle simple local values, and run a real type check. The runtime still follows React’s props, state and events. Validate values arriving from outside the checked program rather than trusting an annotation.
Continue with runtime form validation and behavior-focused component checks to cover the boundaries static checking does not establish.
Reaching the recap and completing the quizzes are separate milestones.