A component's props are its public interface, and JSX gives no help in honoring it. Nothing in <PriceTag amount={1999} /> says whether the prop is called amount, whether it wants dollars or cents, or whether it is required. In JavaScript you find out by reading the component — and find out you read it wrong when the page renders $NaN.
interface PriceTagProps {
amountCents: number;
currency: 'USD' | 'EUR';
}
export function PriceTag({ amountCents, currency }: PriceTagProps) {
return <span>{currency === 'USD' ? '$' : '€'}{(amountCents / 100).toFixed(2)}</span>;
}
export const cart = (
<>
<PriceTag amountCents="1999" currency="USD" />
<PriceTag amountCents={1999} currency="GBP" />
<PriceTag amountCents={1999} />
</>
);src/why.tsx(11,15): error TS2322: Type 'string' is not assignable to type 'number'.
src/why.tsx(12,34): error TS2322: Type '"GBP"' is not assignable to type '"EUR" | "USD"'.
src/why.tsx(13,6): error TS2741: Property 'currency' is missing in type
'{ amountCents: number; }' but required in type 'PriceTagProps'.Three classes of bug, three errors, none needing the app to run: a value of the wrong type (the classic "1999" / 100), a value outside the allowed set, and a missing required prop. Misspell the prop name and you get a fourth. The second repays a look — 'USD' | 'EUR' is a union of literal types, so the compiler knows which strings are legal and your editor offers both. Rename amountCents and it lists every caller.
Types are erased before the code runs, so they describe your source and nothing else; two boundaries lie outside their reach.

await res.json() is typed any, so const user: User = await res.json() is a claim, not a check: a server that renames a field breaks the app as it would without TypeScript. Validate at the boundary with a schema library such as Zod 44,027 (github.com/colinhacks/zod (https://github.com/colinhacks/zod 44,027 )): UserSchema.parse(await res.json()) throws on bad data and returns a value typed from the schema — one declaration serving as both.