Skip to content

Commit da82f9f

Browse files
authored
Add types for JSX support
Closes GH-3. Reviewed-by: Christian Murphy <[email protected]> Reviewed-by: Titus Wormer <[email protected]>
1 parent 5311151 commit da82f9f

File tree

5 files changed

+125
-7
lines changed

5 files changed

+125
-7
lines changed

readme.md

+19
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,21 @@ var x = require('xastscript')
231231
console.log(<music />)
232232
```
233233

234+
For [TypeScript][], this can be done by setting `"jsx": "react"`,
235+
`"jsxFactory": "x"`, and `"jsxFragmentFactory": "null"` in the compiler options.
236+
For more details on configuring JSX for TypeScript, see the
237+
[TypeScript JSX handbook page][].
238+
239+
TypeScript also lets you configure this in a script:
240+
241+
```tsx
242+
/** @jsx x */
243+
/** @jsxFrag null */
244+
import * as x from 'xastscript'
245+
246+
console.log(<music />)
247+
```
248+
234249
## Security
235250

236251
XML can be a dangerous language: don’t trust user-provided data.
@@ -327,3 +342,7 @@ abide by its terms.
327342
[babel]: https://github.com/babel/babel
328343

329344
[babel-jsx]: https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-react-jsx
345+
346+
[typescript]: https://www.typescriptlang.org
347+
348+
[typescript jsx handbook page]: https://www.typescriptlang.org/docs/handbook/jsx.html

types/index.d.ts

+58-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
// TypeScript Version: 3.7
1+
// TypeScript Version: 4.0
22

3-
import {Element, Node, Root} from 'xast'
3+
import * as xast from 'xast'
44

5-
type Children = string | Node | number | Children[]
5+
type Children = string | xast.Node | number | Children[]
66

77
type Primitive = null | undefined | string | number
88

@@ -17,15 +17,15 @@ type Attributes = Record<string, Primitive>
1717
* @param name Qualified name. Case sensitive and can contain a namespace prefix (such as rdf:RDF).
1818
* @param children (Lists of) child nodes. When strings are encountered, they are mapped to Text nodes.
1919
*/
20-
declare function xastscript(name: string, ...children: Children[]): Element
20+
declare function xastscript(name: string, ...children: Children[]): xast.Element
2121

2222
/**
2323
* Create XML trees in xast.
2424
*
2525
* @param name Qualified name. Case sensitive and can contain a namespace prefix (such as rdf:RDF).
2626
* @param children (Lists of) child nodes. When strings are encountered, they are mapped to Text nodes.
2727
*/
28-
declare function xastscript(name: null, ...children: Children[]): Root
28+
declare function xastscript(name: null, ...children: Children[]): xast.Root
2929

3030
/**
3131
* Create XML trees in xast.
@@ -38,6 +38,58 @@ declare function xastscript(
3838
name: string,
3939
attributes?: Attributes,
4040
...children: Children[]
41-
): Element
41+
): xast.Element
42+
43+
/**
44+
* This unique symbol is declared to specify the key on which JSX children are passed, without conflicting
45+
* with the Attributes type.
46+
*/
47+
declare const children: unique symbol
48+
49+
/**
50+
* This namespace allows to use `xastscript` as a JSX implementation.
51+
*
52+
* This namespace is only used to support the use as JSX. It’s **not** intended for direct usage.
53+
*/
54+
declare namespace xastscript.JSX {
55+
/**
56+
* This defines the return value of JSX syntax.
57+
*/
58+
type Element = xast.Element | xast.Root
59+
60+
/**
61+
* This disallows the use of functional components.
62+
*/
63+
type IntrinsicAttributes = never
64+
65+
/**
66+
* This defines the prop types for known elements.
67+
*
68+
* For `xastscript` this defines any string may be used in combination with `xast` `Attributes`.
69+
*
70+
* This **must** be an interface.
71+
*/
72+
// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
73+
interface IntrinsicElements {
74+
[tagName: string]:
75+
| Attributes
76+
| {
77+
/**
78+
* The prop that matches `ElementChildrenAttribute` key defines the type of JSX children, defines the children type.
79+
*/
80+
[children]?: Children
81+
}
82+
}
83+
84+
/**
85+
* The key of this interface defines as what prop children are passed.
86+
*/
87+
interface ElementChildrenAttribute {
88+
/**
89+
* Only the key matters, not the value.
90+
*/
91+
[children]?: never
92+
}
93+
}
4294

4395
export = xastscript

types/test-jsx.tsx

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import {Element, Root} from 'xast'
2+
import * as x from 'xastscript'
3+
4+
const xmlns = 'http://www.sitemaps.org/schemas/sitemap/0.9'
5+
6+
let jsx: Element | Root
7+
jsx = <urlset />
8+
jsx = <urlset xmlns={xmlns} />
9+
jsx = <urlset>string</urlset>
10+
jsx = <urlset>{['string', 'string']}</urlset>
11+
jsx = (
12+
<urlset xmlns={xmlns}>
13+
<child />
14+
</urlset>
15+
)
16+
jsx = (
17+
<urlset>
18+
<loc />
19+
string
20+
</urlset>
21+
)
22+
jsx = (
23+
<urlset>
24+
<loc />
25+
</urlset>
26+
)
27+
jsx = (
28+
<urlset>
29+
<loc />
30+
<loc />
31+
</urlset>
32+
)
33+
jsx = <urlset>{[<loc />, <loc />]}</urlset>
34+
jsx = <urlset>{[]}</urlset>
35+
jsx = <></>
36+
37+
jsx = <foo invalid={{}}></foo> // $ExpectError
38+
jsx = <foo>{{invalid: 'child'}}</foo> // $ExpectError
39+
40+
const element: Element = <foo /> // $ExpectError
41+
const root: Root = <></> // $ExpectError
42+
43+
declare function Bar(props?: Record<string, unknown>): Element
44+
const bar = <Bar /> // $ExpectError

types/test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import x = require('xastscript')
1+
import * as x from 'xastscript'
22

33
x('urlset') // $ExpectType Element
44
x('urlset', 'string') // $ExpectType Element

types/tsconfig.json

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
{
22
"compilerOptions": {
33
"module": "commonjs",
4+
"jsx": "react",
5+
"jsxFactory": "x",
6+
"jsxFragmentFactory": "null",
47
"lib": ["es2015"],
58
"noImplicitAny": true,
69
"noImplicitThis": true,

0 commit comments

Comments
 (0)