TypeScript and JSX Part III - Typing JSX children
Tags: typescript, jsx, react, javascript
Published: 2019-01-17 23:51:14

---
title: TypeScript and JSX Part III - Typing JSX children
published: true
description: An explanation of how the TypeScript compiler works with JSX.
tags: typescript,jsx,react,javascript
series: TypeScript and JSX
---

In the last post we learned about what kinds of types can be JSX constructors, and now it's time to dive deeper into how children work.

The concept of children in JSX is interesting: it can both be in the form of a JSX attribute, as well as nested inside an upper-level JSX expression:

```tsx
// both are valid ways to write children
const myNestedDiv1 = (
  <div>
    <span>I'm stuck!</span>
  </div>
)

const myNestedDiv2 = (
  <div
    children={<span>I'm stuck!</span>}
  />
)

// what gets rendered?
const myNestedDiv3 = (
  <div
    children={<span>Child A</span>}
  >
    <span>Child B</span>
  </div>
)
```

In `myNestedDiv3`, the runtime result is ambiguous, and could potentially vary between libraries! Remembering that JSX is just a syntactic sugar, in React we have this:

```tsx
// before JSX
const myNestedDiv3Untranspiled = (
  <div
    children={<span>Child A</span>}
  >
    <span>Child B</span>
  </div>
)

// after JSX
const myNestedDiv3Transpiled = React.createElement(
  'div',
  { children: React.createElement('span', null, 'Child A') },
  React.createElement('span', null, 'Child B')
)
```

Thus, it's actually entirely up to the React runtime to figure out how it wants to render the actual markup. It could prioritize over the `children` prop, or it could prioritize the rest parameters, or it could do some kind of merge of both!

Because the concept of children is not enforced by the JSX specification, TypeScript provides a way to typecheck the nested JSX expressions against the __element attributes type__. The element attributes type is the type of all attributes added to a JSX expression, i.e. it's the type of the object passed into the second parameter in `React.createElement`.

This typechecking is done by specifying a property under the `JSX` namespace (see the previous post for more info on the `JSX` namespace) called `ElementChildrenAttribute`. With a guiding example, let's say your `JSX` namespace is this:
```ts
namespace JSX {
  interface ElementChildrenAttribute {
    enfants: {} // 'children' in french
  }
}
```

And you have a component defined like so:
```tsx
interface Props {
  enfants?: JSX.Element | JSX.Element[]
  children?: string | string[]
}

function MyComponent(props: Props) { 
  return <div>{props.enfants}</div>
}
```

Then the following will happen:
```tsx
// error! the type `string` is not assignable 
// to the type `JSX.Element | JSX.Element[]` for the `enfants` attribute
const myComponentElement1 = <MyComponent>hello world!</MyComponent>

// good! `enfants` is of type `JSX.Element` which is assignable
// to itself or on array of itself
const myComponentElement2 = (
  <MyComponent>
    <MyComponent />
  </MyComponent>
)
```

So this is TypeScript's way of defining a connection between nested JSX and some property in the interface that you declare.

How is it that when you use React, you always seem to be able to access `this.props.children` in a `React.Component`? Well that's actually just part of the React types themselves, and not some intrinsic feature of TypeScript itself. The React type definitions automatically inject `children` as an optional property for convenience.

In the example above, we defined some function `MyComponent` that takes a parameter called `props` with a specific interface. It returns JSX so it's a valid JSX constructor type.

How does TypeScript know that you can't just add any random attribute to a `<MyComponent />` element? That `enfants`, `children`, and `key` are the only ones you can add?

In the next post in the series, we'll learn more about how TypeScript collects all of the possible attributes (and their types) you can add to a component element.