Describe the benefits of using TypeScript with React.
469 views
01
01
Understand the problem
Question presented to candidate:
"What does TypeScript actually buy you in a React codebase?"
What a strong answer should cover:
Props become a checked contract. A component's prop types are its API, and every call site is verified against it — wrong types, missing required props, and values outside a union are all compile errors.
Typos in prop names are caught, which JSX otherwise swallows silently: an unknown prop just does nothing.
State setters are typed, so setCount("many") fails rather than corrupting state at runtime.
Discriminated unions model impossible states away — a loading union means you cannot read data before checking the status.
Refactoring becomes mechanical. Rename a prop and every call site errors; that is the difference between a rename and an audit.
Editor support is the daily win — autocomplete for props, jump to definition, inline docs. Often more valuable in practice than the error catching.
Honest costs: build and check time, typing complexity for generic components, and any quietly reintroducing every problem.
The rule of thumb: type the boundaries — props, API responses, context values — and let inference handle the interior.
Clarifying questions expected:
"Is this a new codebase or a migration?" — the answers differ substantially.
"How strict are we willing to be?" — strict off removes most of the value.
Code / implementation expected: Optional. Showing a props type and a discriminated union is enough.
typescripttype safetydevelopmenttooling
02
02
Attempt it yourself
Sketch your approach before reading the solution — that's what interviews test.
Nudge consolestandby
Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.
03
03
Study the solution
Target Audience: Engineers preparing for React interviews — assumes basic TypeScript familiarity.
Difficulty: Medium
How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. Section 3 is five real compiler diagnostics, produced by running
Solution ready — 2 min read
Classified // press E to declassify
04
04
Explore the playground snippets
The five diagnostics, with the fixed version beside each
import { useState } from"react";
// This playground runs JavaScript, so none of the errors below are enforced// here — which is precisely the point. Each row shows what plain JSX does with// a mistake, and the exact diagnostic TypeScript produced for it.constBADGE_TYPE = `type BadgeProps = {
label: string;
count: number;
tone?: "info" | "warn";
};`;
constCASES = [
{
wrong: '<Badge label="items" count="3" />',
right: '<Badge label="items" count={3} />',
error: "TS2322: Type 'string' is not assignable to type 'number'.",
js: "renders 'items: 3' — looks fine until you do arithmetic on it",
},
{
wrong: '<Badge label="items" />',
right: '<Badge label="items" count={0} />',
error: "TS2741: Property 'count' is missing in type '{ label: string; }' but required in type 'BadgeProps'.",
js: "renders 'items: ' with nothing after it",
},
{
wrong: '<Badge label="items" count={n} tone="danger" />',
right: '<Badge label="items" count={n} tone="warn" />',
error: "TS2322: Type '\"danger\"' is not assignable to type '\"info\" | \"warn\" | undefined'.",
js: "sets data-tone='danger', which no stylesheet matches — silently unstyled",
},
{
wrong: '<Badge lable="items" count={n} />',
right: '<Badge label="items" count={n} />',
error: "TS2322: Property 'lable' does not exist on type 'IntrinsicAttributes & BadgeProps'. Did you mean 'label'?",
js: "renders ': 3' — the typo is IGNORED entirely. The worst one.",
},
{
wrong: 'onClick={() => setN("many")}',
right: 'onClick={() => setN(n + 1)}',
error: "TS2345: Argument of type 'string' is not assignable to parameter of type 'SetStateAction<number>'.",
js: "state becomes the string 'many'; the next n + 1 gives 'many1'",
},
];
exportdefaultfunctionApp() {
const [i, setI] = useState(3);
const c = CASES[i];
return (
<divstyle={{padding:24, fontFamily: "system-ui", lineHeight:1.6, maxWidth:620 }}><prestyle={{background: "#eef", border: "1pxsolid #ccd", borderRadius:8, padding:10, fontSize:12 }}>
{BADGE_TYPE}
</pre><divstyle={{display: "flex", gap:6, flexWrap: "wrap", margin: "10px0" }}>
{CASES.map((_, n) => (
<buttonkey={n}onClick={() => setI(n)} style={{ fontWeight: n === i ? "bold" : "normal" }}>
case {n + 1}
</button>
))}
</div><divstyle={{border: "1pxsolid #ddd", borderRadius:8, padding:12, fontSize:13 }}><divstyle={{marginBottom:8 }}><spanstyle={{color: "#a33" }}>❌ </span><code>{c.wrong}</code></div><divstyle={{background: "#fdf0f0", border: "1pxsolid #e0b4b4", borderRadius:6, padding:8,
fontSize:12, fontFamily: "ui-monospace, monospace", marginBottom:8 }}>
{c.error}
</div><divstyle={{marginBottom:8, color: "#666", fontSize:12 }}><strong>without types:</strong> {c.js}
</div><div><spanstyle={{color: "#161" }}>✅ </span><code>{c.right}</code></div></div><pstyle={{fontSize:13, color: "#666" }}>
Case 4 is the one to remember. An unknown prop in JSX is not an error —
it is simply dropped, so the component renders with a missing label and
nothing anywhere tells you. Every diagnostic above was produced by
actually compiling this component with the wrong usage.
</p></div>
);
}