Capítulo 22 de 36
TypeScript understands JSX natively in .tsx files — type-checking both intrinsic elements (lowercase, e.g. <div>) and value-based components (uppercase, e.g. <MyComponent>) against a configurable JSX namespace, with the compiled output shape controlled independently by the jsx compiler option.
.tsx and set the jsx compiler option. Five modes trade off output shape:
preserve — keeps JSX as-is for a downstream transform (Babel etc.), emits .jsx.react (classic runtime) — emits React.createElement(...) calls directly, emits .js, no separate transform step needed.react-native — keeps JSX like preserve but emits .js.react-jsx / react-jsxdev — the automatic runtime, emits calls to an auto-imported jsx/jsxDEV helper instead of requiring React in scope.as instead of angle-bracket assertions: <Foo>bar as a type assertion is ambiguous against JSX syntax, so TypeScript disallows angle-bracket assertions entirely in .tsx files — always use bar as Foo there (works identically in .ts files too).<div />) is intrinsic, looked up on JSX.IntrinsicElements; uppercase (<MyComponent />) is value-based, resolved as an identifier in scope. If JSX.IntrinsicElements isn't declared at all, intrinsic elements go unchecked entirely; if it is declared, an unlisted tag name is a compile error (a catch-all [elemName: string]: any index signature reopens that).JSX namespace's location depends on the jsx mode: classic-runtime modes (preserve/react/react-native) look for JSX nested under whatever jsxFactory names (React.JSX for React's default React.createElement factory); automatic-runtime modes (react-jsx/react-jsxdev) instead expect the framework to export a JSX namespace from its jsx-runtime/jsx-dev-runtime entry points. Either way, if the namespace isn't found where expected, both runtimes fall back to a global JSX namespace.props and whose return type is assignable to JSX.Element, including via overloads); only if that fails does it try resolving as a Class Component.new (construct signature) or plain call (call signature) on it returns. The instance type must be assignable to JSX.ElementClass (default {} — permissive; a framework can narrow it, e.g. requiring a render method, to reject non-component values used as JSX tags).JSX.IntrinsicElements[tagName]; for value-based elements, it's a specific property of the element instance type, named by JSX.ElementAttributesProperty (defaulting, since TS 2.8, to the constructor's/function's first parameter type if that interface isn't declared). Required/optional props are enforced normally; attribute names that aren't valid JS identifiers (like data-*) are never flagged even if unknown, since they can't correspond to a declared prop name anyway. JSX.IntrinsicAttributes (and the per-class-instance JSX.IntrinsicClassAttributes<T>) model framework-injected extras that aren't real component props — React's key and ref are the canonical examples.children passed between JSX tags maps onto whichever prop name JSX.ElementChildrenAttribute declares (typically children) — its declared type is checked exactly like any other prop, so a component typed to accept a single JSX.Element child will error if given multiple children or mixed text+element children.JSX.Element: the result type of a JSX expression is any unless JSX.Element is declared — even then it's a "black box" type; you can't introspect a JSX expression's own tag/props/children back out of its JSX.Element type.JSX.ElementType (TS 5.1+): overrides what counts as a valid tag to use in JSX (lowercase intrinsic names, function components, class constructors) — independent from what props each one accepts, which is still governed purely by that component's own first parameter type.{...} inside JSX) are ordinary TypeScript expressions and type-checked exactly like any other expression — a type error inside a {} block (e.g. dividing a string by a number) is caught the same as outside JSX.declare namespace JSX {
interface IntrinsicElements {
foo: { requiredProp: string; optionalProp?: number };
}
}
<foo requiredProp="bar" />; // OK
<foo />; // Error: requiredProp missing
<foo requiredProp="bar" unknownProp />; // Error: unknownProp not declared
JSX.IntrinsicElements interface — required/optional/unknown props all validated against it.jsx mode | Output | File ext | Notes |
|---|---|---|---|
preserve | JSX kept as-is | .jsx | for a downstream transform |
react | React.createElement(...) | .js | classic runtime, no extra step |
react-native | JSX kept as-is | .js | |
react-jsx | _jsx(...) (auto-imported) | .js | automatic runtime |
react-jsxdev | _jsxDEV(...) | .js | automatic dev runtime |
as T, never <T>value, for type assertions in .tsx files — angle brackets are reserved for JSX syntax there.JSX.IntrinsicElements[tag] for lowercase tags, or the property named by JSX.ElementAttributesProperty on a component's element instance type for uppercase ones.JSX.Element is intentionally opaque — don't expect to extract a JSX expression's tag/props back out of its own type.jsx compiler mode to your actual build pipeline (classic vs. automatic runtime) — mismatches show up as "JSX namespace not found" or unexpected React import requirements.JSX.Element, FC, component prop patterns.IntrinsicElements, ElementAttributesProperty) all reuse ordinary TypeScript object-type/index-signature rules.