A React file containing JSX must use the .tsx extension (rather than plain .ts), so the compiler knows to parse JSX syntax. A component's props are typically typed with an interface or type alias, and destructured directly in the function signature. Hooks are generic: useState<T> can have its type parameter given explicitly or inferred from the initial value passed in.
Typing component props
A missing required prop, or a prop of the wrong type, is a compile error at every call site — caught long before the component ever renders in a browser.
interface GreetingProps {
name: string;
excited?: boolean; // optional prop
}
function Greeting({ name, excited = false }: GreetingProps) {
return <div>{excited ? `Hi ${name}!!!` : `Hello, ${name}`}</div>;
}
<Greeting name="Alice" />; // fine
<Greeting name="Bob" excited />; // fine
// <Greeting />; // COMPILE ERROR: missing required prop 'name'Typing useState and useEffect
useState infers its type from the initial value when possible; an explicit type argument is needed when the state can be a union that isn't obvious from the initializer alone, like a value that starts as null.
interface User { name: string; }
function UserProfile() {
const [count, setCount] = useState(0); // inferred: number
const [user, setUser] = useState<User | null>(null); // explicit: needed since null alone infers only null
useEffect(() => {
fetchUser().then(setUser);
}, []);
return user ? <p>{user.name}</p> : <p>Loading...</p>;
}Typing event handlers
React's own type definitions provide specific event types (React.ChangeEvent<HTMLInputElement>, etc.), giving full autocomplete on the event object rather than treating it as untyped.
function SearchInput() {
const [query, setQuery] = useState("");
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
setQuery(e.target.value); // e.target correctly typed as HTMLInputElement
}
return <input value={query} onChange={handleChange} />;
}