Skip to content

Commit 57ac2f7

Browse files
Update JSX documentation (#3064)
Co-authored-by: Sebastian Silbermann <[email protected]>
1 parent 8417ff5 commit 57ac2f7

File tree

2 files changed

+108
-15
lines changed
  • packages
    • documentation/copy/en/reference
    • tsconfig-reference/copy/en/options

2 files changed

+108
-15
lines changed

packages/documentation/copy/en/reference/JSX.md

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ In order to use JSX you must do two things.
1818
1. Name your files with a `.tsx` extension
1919
2. Enable the [`jsx`](/tsconfig#jsx) option
2020

21-
TypeScript ships with three JSX modes: `preserve`, `react`, and `react-native`.
22-
These modes only affect the emit stage - type checking is unaffected.
21+
TypeScript ships with several JSX modes: `preserve`, `react` (classic runtime), `react-jsx` (automatic runtime), `react-jsxdev` (automatic development runtime), and `react-native`.
2322
The `preserve` mode will keep the JSX as part of the output to be further consumed by another transform step (e.g. [Babel](https://babeljs.io/)).
2423
Additionally the output will have a `.jsx` file extension.
2524
The `react` mode will emit `React.createElement`, does not need to go through a JSX transformation before use, and the output will have a `.js` file extension.
@@ -70,14 +69,73 @@ This is important for two reasons:
7069
TypeScript uses the [same convention that React does](http://facebook.github.io/react/docs/jsx-in-depth.html#html-tags-vs.-react-components) for distinguishing between these.
7170
An intrinsic element always begins with a lowercase letter, and a value-based element always begins with an uppercase letter.
7271

72+
### The `JSX` namespace
73+
74+
JSX in TypeScript is typed by the `JSX` namespace. The `JSX` namespace may be defined in various places, depending on the `jsx` compiler option.
75+
76+
The `jsx` options `preserve`, `react`, and `react-native` use the type definitions for classic runtime. This means a variable needs to be in scope that’s determined by the `jsxFactory` compiler option. The `JSX` namespace should be specified on the top-most identifier of the JSX factory. For example, React uses the default factory `React.createElement`. This means its `JSX` namespace should be defined as `React.JSX`.
77+
78+
```ts
79+
export function createElement(): any;
80+
81+
export namespace JSX {
82+
//
83+
}
84+
```
85+
86+
And the user should always import React as `React`.
87+
88+
```ts
89+
import * as React from 'react';
90+
```
91+
92+
Preact uses the JSX factory `h`. That means its types should be defined as the `h.JSX`.
93+
94+
```ts
95+
export function h(props: any): any;
96+
97+
export namespace h.JSX {
98+
//
99+
}
100+
```
101+
102+
The user should use a named import to import `h`.
103+
104+
```ts
105+
import { h } from 'preact';
106+
```
107+
108+
For the `jsx` options `react-jsx` and `react-jsxdev`, the `JSX` namespace should be exported from the matching entry points. For `react-jsx` this is `${jsxImportSource}/jsx-runtime`. For `react-jsxdev`, this is `${jsxImportSource}/jsx-dev-runtime`. Since these don’t use a file extension, you must use the [`exports`](https://nodejs.org/api/packages.html#exports) field in `package.json` map in order to support ESM users.
109+
110+
```json
111+
{
112+
"exports": {
113+
"./jsx-runtime": "./jsx-runtime.js",
114+
"./jsx-dev-runtime": "./jsx-dev-runtime.js",
115+
}
116+
}
117+
```
118+
119+
Then in `jsx-runtime.d.ts` and `jsx-dev-runtime.d.ts`:
120+
121+
```ts
122+
export namespace JSX {
123+
//
124+
}
125+
```
126+
127+
Note that while exporting the `JSX` namespace is sufficient for type checking, the production runtime needs the `jsx`, `jsxs`, and `Fragment` exports at runtime, and the development runtime needs `jsxDEV` and `Fragment`. Ideally you add types for those too.
128+
129+
If the `JSX` namespace isn’t available in the appropriate location, both the classic and the automatic runtime fall back to the global `JSX` namespace.
130+
73131
### Intrinsic elements
74132

75133
Intrinsic elements are looked up on the special interface `JSX.IntrinsicElements`.
76134
By default, if this interface is not specified, then anything goes and intrinsic elements will not be type checked.
77135
However, if this interface _is_ present, then the name of the intrinsic element is looked up as a property on the `JSX.IntrinsicElements` interface.
78136
For example:
79137

80-
```ts
138+
```tsx
81139
declare namespace JSX {
82140
interface IntrinsicElements {
83141
foo: any;
@@ -104,7 +162,7 @@ declare namespace JSX {
104162

105163
Value-based elements are simply looked up by identifiers that are in scope.
106164

107-
```ts
165+
```tsx
108166
import MyComponent from "./myComponent";
109167

110168
<MyComponent />; // ok
@@ -123,7 +181,7 @@ Because these two types of value-based elements are indistinguishable from each
123181
As the name suggests, the component is defined as a JavaScript function where its first argument is a `props` object.
124182
TS enforces that its return type must be assignable to `JSX.Element`.
125183

126-
```ts
184+
```tsx
127185
interface FooProp {
128186
name: string;
129187
X: number;
@@ -211,7 +269,7 @@ const myComponent = MyFactoryFunction();
211269
The element instance type is interesting because it must be assignable to `JSX.ElementClass` or it will result in an error.
212270
By default `JSX.ElementClass` is `{}`, but it can be augmented to limit the use of JSX to only those types that conform to the proper interface.
213271

214-
```ts
272+
```tsx
215273
declare namespace JSX {
216274
interface ElementClass {
217275
render: any;
@@ -244,7 +302,7 @@ This is slightly different between intrinsic and value-based elements.
244302

245303
For intrinsic elements, it is the type of the property on `JSX.IntrinsicElements`
246304

247-
```ts
305+
```tsx
248306
declare namespace JSX {
249307
interface IntrinsicElements {
250308
foo: { bar?: boolean };
@@ -262,7 +320,7 @@ It should be declared with a single property.
262320
The name of that property is then used.
263321
As of TypeScript 2.8, if `JSX.ElementAttributesProperty` is not provided, the type of first parameter of the class element's constructor or Function Component's call will be used instead.
264322

265-
```ts
323+
```tsx
266324
declare namespace JSX {
267325
interface ElementAttributesProperty {
268326
props; // specify the property name to use
@@ -283,7 +341,7 @@ class MyComponent {
283341
The element attribute type is used to type check the attributes in the JSX.
284342
Optional and required properties are supported.
285343

286-
```ts
344+
```tsx
287345
declare namespace JSX {
288346
interface IntrinsicElements {
289347
foo: { requiredProp: string; optionalProp?: number };
@@ -304,7 +362,7 @@ Additionally, the `JSX.IntrinsicAttributes` interface can be used to specify ext
304362

305363
The spread operator also works:
306364

307-
```ts
365+
```tsx
308366
const props = { requiredProp: "bar" };
309367
<foo {...props} />; // ok
310368

@@ -326,7 +384,7 @@ declare namespace JSX {
326384
}
327385
```
328386

329-
```ts
387+
```tsx
330388
<div>
331389
<h1>Hello</h1>
332390
</div>;
@@ -345,7 +403,7 @@ const CustomComp = (props) => <div>{props.children}</div>
345403

346404
You can specify the type of _children_ like any other attribute. This will override the default type from, e.g. the [React typings](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react) if you use them.
347405

348-
```ts
406+
```tsx
349407
interface PropsType {
350408
children: JSX.Element
351409
name: string
@@ -386,11 +444,30 @@ You can customize the type by specifying the `JSX.Element` interface.
386444
However, it is not possible to retrieve type information about the element, attributes or children of the JSX from this interface.
387445
It is a black box.
388446

447+
## The JSX function return type
448+
449+
By default, function components must return `JSX.Element | null`. However, this doesn’t always represent runtime behaviour. As of TypeScript 5.1, you can specify `JSX.ElementType` to override what is a valid JSX component type. Note that this doesn’t define what props are valid. The type of props is always defined by the first argument of the component that’s passed. The default looks something like this:
450+
451+
```ts
452+
namespace JSX {
453+
export type ElementType =
454+
// All the valid lowercase tags
455+
keyof IntrinsicAttributes
456+
// Function components
457+
(props: any) => Element
458+
// Class components
459+
new (props: any) => ElementClass;
460+
export interface IntrinsicAttributes extends /*...*/ {}
461+
export type Element = /*...*/;
462+
export type ElementClass = /*...*/;
463+
}
464+
```
465+
389466
## Embedding Expressions
390467

391468
JSX allows you to embed expressions between tags by surrounding the expressions with curly braces (`{ }`).
392469

393-
```ts
470+
```tsx
394471
const a = (
395472
<div>
396473
{["foo", "bar"].map((i) => (
@@ -403,7 +480,7 @@ const a = (
403480
The above code will result in an error since you cannot divide a string by a number.
404481
The output, when using the `preserve` option, looks like:
405482

406-
```ts
483+
```tsx
407484
const a = (
408485
<div>
409486
{["foo", "bar"].map(function (i) {
@@ -418,7 +495,7 @@ const a = (
418495
To use JSX with React you should use the [React typings](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react).
419496
These typings define the `JSX` namespace appropriately for use with React.
420497

421-
```ts
498+
```tsx
422499
/// <reference path="react.d.ts" />
423500

424501
interface Props {

packages/tsconfig-reference/copy/en/options/jsx.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,19 @@ declare module JSX {
9494
// @noErrors
9595
export const HelloWorld = () => <h1>Hello world</h1>;
9696
```
97+
98+
This option can be used on a per-file basis too using an `@jsxRuntime` comment.
99+
100+
Always use the classic runtime (`"react"`) for this file:
101+
102+
```tsx
103+
/* @jsxRuntime classic */
104+
export const HelloWorld = () => <h1>Hello world</h1>;
105+
```
106+
107+
Always use the automatic runtime (`"react-jsx"`) for this file:
108+
109+
```tsx
110+
/* @jsxRuntime automatic */
111+
export const HelloWorld = () => <h1>Hello world</h1>;
112+
```

0 commit comments

Comments
 (0)