|
1 |
| -# Introduction |
2 |
| - |
3 |
| -JSX is an embeddable XML-like syntax. |
4 |
| -It is meant to be transformed into valid JavaScript but the semantics of that transformation are implementation-specific. |
5 |
| -JSX came to popularity with the React library but has since seen other applications. |
6 |
| -TypeScript supports embedding, type checking, and optionally compiling JSX directly into JavaScript. |
7 |
| - |
8 |
| -# Basic usage |
9 |
| - |
10 |
| -In order to use JSX you must do two things. |
11 |
| - |
12 |
| -1. Name your files with the `.tsx` extension |
13 |
| -2. Enable the `jsx` option |
14 |
| - |
15 |
| -TypeScript ships with two JSX modes: `preserve` and `react`. |
16 |
| -These modes only affect the emit stage. |
17 |
| -The `preserve` mode will keep the JSX as part of the output to be further consumed by another transform step. |
18 |
| -Additionally the output will have a `.jsx` file extension. |
19 |
| -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. |
20 |
| - |
21 |
| -Mode | Input | Output | File Extension |
22 |
| ------------|-----------|------------------------------|--------------- |
23 |
| -`preserve` | `<div />` | `<div />` | `.jsx` |
24 |
| -`react` | `<div />` | `React.createElement("div")` | `.js` |
25 |
| - |
26 |
| -You can specify this mode using either the `--jsx` command line flag or the corresponding option in your [tsconfig.json](https://github.com/Microsoft/TypeScript/wiki/tsconfig.json) file. |
27 |
| - |
28 |
| -> *Note: The identifier `React` is hard-coded, so you must make React available with an uppercase R.* |
29 |
| -
|
30 |
| -# The `as` operator |
31 |
| - |
32 |
| -Since TypeScript uses angle brackets for type assertions, there is a conflict when parsing between type assertions and JSX. |
33 |
| -Consider the following code: |
34 |
| - |
35 |
| -```JSX |
36 |
| -var foo = <foo>bar; |
37 |
| -</foo> |
38 |
| -``` |
39 |
| - |
40 |
| -Is this code creating a JSX element with the content of `bar;`, or is it asserting that `bar` is of type `foo` and there is an invalid expression on line 2? |
41 |
| -To simplify cases like this, angle bracket type assertions are not available in `.tsx` files. |
42 |
| -As a result, in a `.tsx` file, the previous code would be interpreted as a JSX element, and in a `.ts` file it would result in an error. |
43 |
| - |
44 |
| -To make up for this loss of functionality in `.tsx` files, a new type assertion operator has been added: `as`. |
45 |
| - |
46 |
| -```TypeScript |
47 |
| -var foo = bar as foo; |
48 |
| -``` |
49 |
| - |
50 |
| -The `as` operator is available in both `.ts` and `.tsx` files. |
51 |
| - |
52 |
| -# Type Checking |
53 |
| - |
54 |
| -In order to understand type checking with JSX you must first understand the difference between intrinsic elements and value-based elements. |
55 |
| -Given a JSX expression `<expr />`, `expr` may either refer to something intrinsic to the environment (e.g. a `div` or `span` in a DOM environment) or to a custom component that you've created. |
56 |
| -This is important for two reasons: |
57 |
| - |
58 |
| -1. For React, intrinsic elements are emitted as strings (`React.createElement("div")`), whereas a component you've created is not (`React.createElement(MyComponent)`). |
59 |
| -2. The types of the attributes being passed in the JSX element should be looked up differently. Intrinsic element attributes should be known *intrinsically* whereas components will likely want to specify their own set of attributes. |
60 |
| - |
61 |
| -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. |
62 |
| -An intrinsic element always begins with a lowercase letter, and a value-based element always begins with an uppercase letter. |
63 |
| - |
64 |
| -## Intrinsic elements |
65 |
| - |
66 |
| -Intrinsic elements are looked up on the special interface `JSX.IntrinsicElements`. |
67 |
| -By default, if this interface is not specified, then anything goes and intrinsic elements will not be type checked. |
68 |
| -However, if interface *is* present, then the name of the intrinsic element is looked up as a property on the `JSX.IntrinsicElements` interface. |
69 |
| -For example: |
70 |
| - |
71 |
| -```TypeScript |
72 |
| -declare namespace JSX { |
73 |
| - interface IntrinsicElements { |
74 |
| - foo: any |
75 |
| - } |
76 |
| -} |
77 |
| - |
78 |
| -<foo />; // ok |
79 |
| -<bar />; // error |
80 |
| -``` |
81 |
| - |
82 |
| -In the above example, `<foo />` will work fine but `<bar />` will result in an error since it has not been specified on `JSX.IntrinsicElements`. |
83 |
| - |
84 |
| -*Note: You can also specify a catch-all string indexer on `JSX.IntrinsicElements`* as follows: |
85 |
| - |
86 |
| -```TypeScript |
87 |
| -declare namespace JSX { |
88 |
| - interface IntrinsicElements { |
89 |
| - [elemName: string]: any; |
90 |
| - } |
91 |
| -} |
92 |
| -``` |
93 |
| - |
94 |
| -## Value-based elements |
95 |
| - |
96 |
| -Value based elements are simply looked up by identifiers that are in scope. |
97 |
| - |
98 |
| -```TypeScript |
99 |
| -import MyComponent from "./myComponent"; |
100 |
| - |
101 |
| -<MyComponent />; // ok |
102 |
| -<SomeOtherComponent />; // error |
103 |
| -``` |
104 |
| - |
105 |
| -It is possible to limit the type of a value-based element. |
106 |
| -However, for this we must introduce two new terms: the *element class type* and the *element instance type*. |
107 |
| - |
108 |
| -Given `<Expr />`, the *element class type* is the type of `Expr`. |
109 |
| -So in the example above, if `MyComponent` was an ES6 class the class type would be that class. |
110 |
| -If `MyComponent` was a factory function, the class type would be that function. |
111 |
| - |
112 |
| -Once the class type is established, the instance type is determined by the union of the return types of the class type's call signatures and construct signatures. |
113 |
| -So again, in the case of an ES6 class, the instance type would be the type of an instance of that class, and in the case of a factory function, it would be the type of the value returned from the function. |
114 |
| - |
115 |
| -```TypeScript |
116 |
| -class MyComponent { |
117 |
| - render() {} |
118 |
| -} |
119 |
| - |
120 |
| -// use a construct signature |
121 |
| -var myComponent = new MyComponent(); |
122 |
| - |
123 |
| -// element class type => MyComponent |
124 |
| -// element instance type => { render: () => void } |
125 |
| - |
126 |
| -function MyFactoryFunction() { |
127 |
| - return { |
128 |
| - render: () => { |
129 |
| - } |
130 |
| - } |
131 |
| -} |
132 |
| - |
133 |
| -// use a call signature |
134 |
| -var myComponent = MyFactoryFunction(); |
135 |
| - |
136 |
| -// element class type => MyFactoryFunction |
137 |
| -// element instance type => { render: () => void } |
138 |
| -``` |
139 |
| - |
140 |
| -The element instance type is interesting because it must be assignable to `JSX.ElementClass` or it will result in an error. |
141 |
| -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. |
142 |
| - |
143 |
| -```TypeScript |
144 |
| -declare module JSX { |
145 |
| - interface ElementClass { |
146 |
| - render: any; |
147 |
| - } |
148 |
| -} |
149 |
| - |
150 |
| -class MyComponent { |
151 |
| - render() {} |
152 |
| -} |
153 |
| -function MyFactoryFunction() { |
154 |
| - return { render: () => {} } |
155 |
| -} |
156 |
| - |
157 |
| -<MyComponent />; // ok |
158 |
| -<MyFactoryFunction />; // ok |
159 |
| - |
160 |
| -class NotAValidComponent {} |
161 |
| -function NotAValidFactoryFunction() { |
162 |
| - return {}; |
163 |
| -} |
164 |
| - |
165 |
| -<NotAValidComponent />; // error |
166 |
| -<NotAValidFactoryFunction />; // error |
167 |
| -``` |
168 |
| - |
169 |
| -## Attribute type checking |
170 |
| - |
171 |
| -The first step to type checking attributes is to determine the *element attributes type*. |
172 |
| -This is slightly different between intrinsic and value-based elements. |
173 |
| - |
174 |
| -For intrinsic elements, it is the type of the property on `JSX.IntrinsicElements` |
175 |
| - |
176 |
| -```TypeScript |
177 |
| -declare module JSX { |
178 |
| - interface IntrinsicElements { |
179 |
| - foo: { bar?: boolean } |
180 |
| - } |
181 |
| -} |
182 |
| - |
183 |
| -// element attributes type for `foo` is `{bar?: boolean}` |
184 |
| -<foo bar />; |
185 |
| -``` |
186 |
| - |
187 |
| -For value-based elements, it is a bit more complex. |
188 |
| -It is determined by the type of a property on the *element instance type* that was previously determined. |
189 |
| -Which property to use is determined by `JSX.ElementAttributesProperty`. |
190 |
| -It should be declared with a single property. |
191 |
| -The name of that property is then used. |
192 |
| - |
193 |
| -```TypeScript |
194 |
| -declare module JSX { |
195 |
| - interface ElementAttributesProperty { |
196 |
| - props; // specify the property name to use |
197 |
| - } |
198 |
| -} |
199 |
| - |
200 |
| -class MyComponent { |
201 |
| - // specify the property on the element instance type |
202 |
| - props: { |
203 |
| - foo?: string; |
204 |
| - } |
205 |
| -} |
206 |
| - |
207 |
| -// element attributes type for `MyComponent` is `{foo?: string}` |
208 |
| -<MyComponent foo="bar" /> |
209 |
| -``` |
210 |
| - |
211 |
| -The element attribute type is used to type check the attributes in the JSX. |
212 |
| -Optional and required properties are supported. |
213 |
| - |
214 |
| -```TypeScript |
215 |
| -declare module JSX { |
216 |
| - interface IntrinsicElements { |
217 |
| - foo: { requiredProp: string; optionalProp?: number } |
218 |
| - } |
219 |
| -} |
220 |
| - |
221 |
| -<foo requiredProp="bar" />; // ok |
222 |
| -<foo requiredProp="bar" optionalProp={0} />; // ok |
223 |
| -<foo />; // error, requiredProp is missing |
224 |
| -<foo requiredProp={0} />; // error, requiredProp should be a string |
225 |
| -<foo requiredProp="bar" unknownProp />; // error, unknownProp does not exist |
226 |
| -<foo requiredProp="bar" some-unknown-prop />; // ok, because `some-unknown-prop` is not a valid identifier |
227 |
| -``` |
228 |
| - |
229 |
| ->*Note: If an attribute name is not a valid JS identifier (like a `data-*` attribute), it is not considered to be an error if it is not found in the element attributes type.* |
230 |
| -
|
231 |
| -The spread operator also works: |
232 |
| - |
233 |
| -```JSX |
234 |
| -var props = { requiredProp: 'bar' }; |
235 |
| -<foo {...props} />; // ok |
236 |
| - |
237 |
| -var badProps = {}; |
238 |
| -<foo {...badProps} />; // error |
239 |
| -``` |
240 |
| - |
241 |
| -# The JSX result type |
242 |
| - |
243 |
| -By default the result of a JSX expression is typed as `any`. |
244 |
| -You can customize the type by specifying the `JSX.Element` interface. |
245 |
| -However, it is not possible to retrieve type information about the element, attributes or children of the JSX from this interface. |
246 |
| -It is a black box. |
247 |
| - |
248 |
| -# Escaping to TypeScript |
249 |
| - |
250 |
| -JSX in JavaScript allows you to escape to JavaScript by using curly braces `{ }`. |
251 |
| -JSX in TypeScript allows you to do the same thing, but you escape to TypeScript. |
252 |
| -That means transpilation features and type checking still work when embedded within JSX. |
253 |
| - |
254 |
| -```JSX |
255 |
| -var a = <div> |
256 |
| - {['foo', 'bar'].map(i => <span>{i/2}</span>)} |
257 |
| -</div> |
258 |
| -``` |
259 |
| - |
260 |
| -The above code will result in an error since you cannot divide a string by a number. |
261 |
| -The output, when using the `preserve` option, looks like: |
262 |
| - |
263 |
| -```JSX |
264 |
| -var a = <div> |
265 |
| - {['foo', 'bar'].map(function (i) { return <span>{i / 2}</span>; })} |
266 |
| -</div> |
267 |
| -``` |
268 |
| - |
269 |
| -# React integration |
270 |
| - |
271 |
| -To use JSX with React you should use the [React typings](https://github.com/borisyankov/DefinitelyTyped/tree/master/react). |
272 |
| -These typings define the `JSX` namespace appropriately for use with React. |
273 |
| - |
274 |
| -```TypeScript |
275 |
| -/// <reference path="react.d.ts" /> |
276 |
| - |
277 |
| -interface Props { |
278 |
| - foo: string; |
279 |
| -} |
280 |
| - |
281 |
| -class MyComponent extends React.Component<Props, {}> { |
282 |
| - render() { |
283 |
| - return <span>{this.props.foo}</span> |
284 |
| - } |
285 |
| -} |
286 |
| - |
287 |
| -<MyComponent foo="bar" />; // ok |
288 |
| -<MyComponent foo={0} />; // error |
289 |
| -``` |
| 1 | +> ### This page has moved to http://www.typescriptlang.org/docs/handbook/jsx.html |
0 commit comments