diff --git a/readme.md b/readme.md
index 96098b5..6178401 100644
--- a/readme.md
+++ b/readme.md
@@ -231,6 +231,21 @@ var x = require('xastscript')
console.log()
```
+For [TypeScript][], this can be done by setting `"jsx": "react"`,
+`"jsxFactory": "x"`, and `"jsxFragmentFactory": "null"` in the compiler options.
+For more details on configuring JSX for TypeScript, see the
+[TypeScript JSX handbook page][].
+
+TypeScript also lets you configure this in a script:
+
+```tsx
+/** @jsx x */
+/** @jsxFrag null */
+import * as x from 'xastscript'
+
+console.log()
+```
+
## Security
XML can be a dangerous language: don’t trust user-provided data.
@@ -327,3 +342,7 @@ abide by its terms.
[babel]: https://github.com/babel/babel
[babel-jsx]: https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-react-jsx
+
+[typescript]: https://www.typescriptlang.org
+
+[typescript jsx handbook page]: https://www.typescriptlang.org/docs/handbook/jsx.html
diff --git a/types/index.d.ts b/types/index.d.ts
index 34d047c..a9ed093 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -1,8 +1,8 @@
-// TypeScript Version: 3.7
+// TypeScript Version: 4.0
-import {Element, Node, Root} from 'xast'
+import * as xast from 'xast'
-type Children = string | Node | number | Children[]
+type Children = string | xast.Node | number | Children[]
type Primitive = null | undefined | string | number
@@ -17,7 +17,7 @@ type Attributes = Record
* @param name Qualified name. Case sensitive and can contain a namespace prefix (such as rdf:RDF).
* @param children (Lists of) child nodes. When strings are encountered, they are mapped to Text nodes.
*/
-declare function xastscript(name: string, ...children: Children[]): Element
+declare function xastscript(name: string, ...children: Children[]): xast.Element
/**
* Create XML trees in xast.
@@ -25,7 +25,7 @@ declare function xastscript(name: string, ...children: Children[]): Element
* @param name Qualified name. Case sensitive and can contain a namespace prefix (such as rdf:RDF).
* @param children (Lists of) child nodes. When strings are encountered, they are mapped to Text nodes.
*/
-declare function xastscript(name: null, ...children: Children[]): Root
+declare function xastscript(name: null, ...children: Children[]): xast.Root
/**
* Create XML trees in xast.
@@ -38,6 +38,58 @@ declare function xastscript(
name: string,
attributes?: Attributes,
...children: Children[]
-): Element
+): xast.Element
+
+/**
+ * This unique symbol is declared to specify the key on which JSX children are passed, without conflicting
+ * with the Attributes type.
+ */
+declare const children: unique symbol
+
+/**
+ * This namespace allows to use `xastscript` as a JSX implementation.
+ *
+ * This namespace is only used to support the use as JSX. It’s **not** intended for direct usage.
+ */
+declare namespace xastscript.JSX {
+ /**
+ * This defines the return value of JSX syntax.
+ */
+ type Element = xast.Element | xast.Root
+
+ /**
+ * This disallows the use of functional components.
+ */
+ type IntrinsicAttributes = never
+
+ /**
+ * This defines the prop types for known elements.
+ *
+ * For `xastscript` this defines any string may be used in combination with `xast` `Attributes`.
+ *
+ * This **must** be an interface.
+ */
+ // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
+ interface IntrinsicElements {
+ [tagName: string]:
+ | Attributes
+ | {
+ /**
+ * The prop that matches `ElementChildrenAttribute` key defines the type of JSX children, defines the children type.
+ */
+ [children]?: Children
+ }
+ }
+
+ /**
+ * The key of this interface defines as what prop children are passed.
+ */
+ interface ElementChildrenAttribute {
+ /**
+ * Only the key matters, not the value.
+ */
+ [children]?: never
+ }
+}
export = xastscript
diff --git a/types/test-jsx.tsx b/types/test-jsx.tsx
new file mode 100644
index 0000000..68dd089
--- /dev/null
+++ b/types/test-jsx.tsx
@@ -0,0 +1,44 @@
+import {Element, Root} from 'xast'
+import * as x from 'xastscript'
+
+const xmlns = 'http://www.sitemaps.org/schemas/sitemap/0.9'
+
+let jsx: Element | Root
+jsx =
+jsx =
+jsx = string
+jsx = {['string', 'string']}
+jsx = (
+
+
+
+)
+jsx = (
+
+
+ string
+
+)
+jsx = (
+
+
+
+)
+jsx = (
+
+
+
+
+)
+jsx = {[, ]}
+jsx = {[]}
+jsx = <>>
+
+jsx = // $ExpectError
+jsx = {{invalid: 'child'}} // $ExpectError
+
+const element: Element = // $ExpectError
+const root: Root = <>> // $ExpectError
+
+declare function Bar(props?: Record): Element
+const bar = // $ExpectError
diff --git a/types/test.ts b/types/test.ts
index 524b9f5..437a9f6 100644
--- a/types/test.ts
+++ b/types/test.ts
@@ -1,4 +1,4 @@
-import x = require('xastscript')
+import * as x from 'xastscript'
x('urlset') // $ExpectType Element
x('urlset', 'string') // $ExpectType Element
diff --git a/types/tsconfig.json b/types/tsconfig.json
index bb008c0..5d74cc2 100644
--- a/types/tsconfig.json
+++ b/types/tsconfig.json
@@ -1,6 +1,9 @@
{
"compilerOptions": {
"module": "commonjs",
+ "jsx": "react",
+ "jsxFactory": "x",
+ "jsxFragmentFactory": "null",
"lib": ["es2015"],
"noImplicitAny": true,
"noImplicitThis": true,