TypeScriptの型におけるJSXサポートが100%分かる記事

**TypeScript**はJavaScriptに静的型を付けることができるAltJSです。2015年9月に登場したTypeScript 1.6では**JSX**のサポートが搭載され、`.tsx`という拡張子を用いることでJSXを含むコードを書いたり型チェックしたりすることができます。

JSXはJavaScriptに対してHTML（あるいはXML）のタグのような構文を導入する拡張記法です。以下の例のようにJavaScriptプログラム中に式としてタグを書くことができます（https://facebook.github.io/jsx/ から引用）：

```jsx
// Using JSX to express UI components.
var dropdown =
  <Dropdown>
    A dropdown list
    <Menu>
      <MenuItem>Do Something</MenuItem>
      <MenuItem>Do Something Fun!</MenuItem>
      <MenuItem>Do Something Else</MenuItem>
    </Menu>
  </Dropdown>;

render(dropdown);
```
この例では`<Dropdown>...</Dropdown>`の部分がひとつの式であり、`dropdown`変数に代入されています。JSXの使い道として最もよく知られているのは**React**でしょう。ReactはUIライブラリであり、ちょうど上の例のようにJSXを使って構築したUIをレンダリングすることができます。

この記事のテーマはJSXと型の関係です。TypeScriptでは、JSX部分に対してもちゃんと型チェックを行ってくれるのが特徴的です。再びReactを例に用います。

```tsx
import * as React from 'react';

const MyComponent: React.FunctionComponent<{
  foo: number;
  bar: string;
}> = ({foo, bar}) => <p>My component {foo} {bar}</p>;

const elm = <MyComponent
  foo="abc" // ←ここで型エラー（fooには文字列ではなく数値を渡さないといけないので）
  bar="def"
/>;
```
このコードでは、`MyComponent`を2つのprops, `foo`と`bar`を受け取るコンポーネントとして型を定義しています。下半分で`MyComponent`をコンポーネントとしてJSXから使用しています。このようにHTMLの属性のような記法でpropsをコンポーネントに渡すことができます。このコードでは`foo`も`bar`も文字列を渡しており、`MyComponent`の型と違っているのでエラーとなります。

ほとんどの場合TypeScriptのJSXサポートはReactと組み合わせて使用しますが、React以外にもJSXと組み合わせられるライブラリは存在しており（[preact](https://github.com/developit/preact)とか）、TypeScriptはそれらと一緒に使うことも可能です。それゆえに、TypeScriptのJSXサポートはReact専用のものではなくJSXという記法に対して一般化されたものとなっています。Reactやpreact用の型定義は、実はTypeScriptが型の上で提供する特別なインターフェースを用いることで、JSXに対する適切な型付けを実現しています。

**この記事では、JSXの型チェックのためにTypeScriptから提供されているインターフェースについて解説します**。この記事を完全に理解することで、`@types/react`のようなJSXサポートを含む型定義を書けるようになるでしょう。

この記事の内容の大部分は[TypeScriptハンドブック](https://www.typescriptlang.org/docs/handbook/jsx.html)（[日本語訳した人もいるようです](https://qiita.com/murank/items/90164aa9a3bed3c49a80)）に書いてある内容ですが、より分かりやすく噛み砕いて説明して理解率100%を目指します。

## 組み込み要素

いきなりですが、次のコードは組み込み要素を定義するコード例です。

```tsx
declare namespace JSX {
    interface IntrinsicElements {
        foo: {
            hoge: string;
            fuga: number;
        }
    }
}

const elm = <foo hoge="文字列" fuga={123} />;
```

このコードでは`JSX`という名前空間を宣言し、その中の`IntrinsicElements`インターフェースを定義しています。実は、この**JSX**という名前空間がTypeScriptが提供するJSXサポートの要です。この中に特定の名前のインターフェースを定義することによって、TypeScriptへJSXの型チェックに関する指示を出すことができます。

`JSX`名前空間の中に`IntrinsicElements`というインターフェースを作ることで組み込み要素を定義します。今回このインターフェースは`foo`というプロパティのみ持っているため、今回存在する組み込み要素は`foo`だけです。なお、組み込み要素というのは名前が小文字の要素で、ReactではHTML要素（`<div>`とか`<span>`とか）に相当します。

各プロパティの型をオブジェクトとすることで、その組み込み要素に渡すことができる属性（props）を指定します。この場合、`foo`要素には`hoge`という属性を文字列で、そして`fuga`という属性を数値で与えなければいけません。以下のようなものはエラーとなります。

```tsx
// エラー（hoge属性の型が違うので）
const elm2 = <foo hoge={123} fuga={456} />
// エラー（barという組み込み要素は存在しないので）
const elm3 = <bar />;
```

## JSX式の結果の型

そもそも、JSX式の結果は何でしょうか。つまり、例えば以下の変数`elm`の型は何になるのでしょうか。

```tsx
const elm = <foo hoge="文字列" fuga={123} />;
```

調べてみると、`elm`の型は`JSX.Element`です。お察しの通り、上の`JSX`名前空間の下に`Element`型を定義してやることで、JSX式の型を自分で定義できるのです。

例えば、思い切ってJSX式の結果を`string`型にしてみましょう。すると、`elm`の型が`string`になります。

なお、以降の例も単体でコンパイルできるように`declare namespace JSX`全体の記載がありますが、前回との変更点はコメントで明記しますので全部読みなおさなくても大丈夫です。

```tsx
declare namespace JSX {
    // JSX.Elementを定義
    type Element = string;
    interface IntrinsicElements {
        foo: {
            hoge: string;
            fuga: number;
        }
    }
}

// elmの型はstring
const elm = <foo hoge="文字列" fuga={123} />;
```

JSX式が直接HTML文字列になるおもしろいテンプレートエンジンを作りたくなったときはこのような型定義が役に立つでしょう。

なお、どんな要素でもJSX式の結果は`JSX.Element`です。`<foo />`と`<bar />`で結果が変わるというようなものは作ることができません。

## 子要素の型

いままで出てきた`foo`要素は全部最後が`/>`で終わっていした。これはXMLなどでも見られる記法で、中身が何もないときに開始タグと終了タグをまとめて書ける記法です。

しかし、JSXでは要素が子要素を持つこともできます。今の状態では、子要素には何でも入れることができます。

```tsx
declare namespace JSX {
  // Elementの型を変更した（文字列は都合が悪いので）
  type Element = {
    this_is_element: true;
  };
  interface IntrinsicElements {
    foo: {
      hoge: string;
      fuga: number;
    };
    // bar要素を追加した
    bar: {};
  }
}

const elm = (
  <foo hoge="文字列" fuga={123}>
    <bar>文字列</bar>
  </foo>
);
```

この場合、`foo`要素の子は1つの`bar`要素であり、`bar`要素の子は1つの文字列です。

実は、要素の子がどうなるべきかを型で制限することが可能です。そのためには2つのことを行う必要があります。

まず`JSX.ElementChildrenAttribute`という特殊なインターフェースを定義します。これはただ1つのプロパティを持つインターフェースであり、そのプロパティ名が子を表すプロパティ名として宣言されます。

そして、各要素の属性の宣言時にそのプロパティ名を用いて子の型を指定します。

実際にやってみると次のようになります。以下では`children`という名前を子を表すプロパティ名として宣言しました（これはReactと同じです）。

```tsx
declare namespace JSX {
  interface ElementChildrenAttribute {
    // childrenという名前を子を表すプロパティ名として宣言
    children: any;
  }
  type Element = {
    this_is_element: true;
  };
  interface IntrinsicElements {
    foo: {
      hoge: string;
      fuga: number;
      // childrenプロパティで子の型を指定
      // （foo要素の子は別の要素）
      children: JSX.Element;
    };
    bar: {
      // barの子は文字列
      children: string;
    };
  }
}

const elm = (
  <foo hoge="文字列" fuga={123}>
    <bar>文字列</bar>
  </foo>
);

// エラー（fooの子が別の要素ではなく文字列になっているので）
const elm2 = (
  <foo hoge="文字列" fuga={123}>
    文字列
  </foo>
);
// エラー（barの子が文字列ではなく数値になっているので）
const elm3 = <bar>{456}</bar>;
// エラー（barの子が無いので）
const elm4 = <bar />;
```

お察しの通り、JSX式で書かれた各要素に対してはその要素に指定された属性たちを集めたオブジェクト型が作られます。例えば`<bar>文字列</bar>`に対しては、属性は何も無くて子が文字列なので`{ children: string; }`というオブジェクト型になります。そしてこれが`JSX.IntrinsicElements.bar`に合致する（代入可能）かが調べることによって型チェックが行なわれます。

`<bar />`のように`bar`の子が無い場合は、属性も無く子も無いので`{}`型になります。これは`JSX.IntrinsicElements.bar`、すなわち`{ children: string; }`には代入できないので型エラーとなります。

`<foo hoge="文字列" fuga={123}>文字列</foo>`のように属性がある場合も同様です。この場合与えられた属性たちの型は`{ hoge: string; fuga: number; children: string; }`となります。これは`JSX.IntrinsicElements.foo`とは合致しない（`children`の型が違う）ので型エラーとなります。

このことが分かれば、要素の宣言において省略可能な子や省略可能な属性を宣言することができます。次の例では`foo`や`bar`の属性や子要素の型をいじってみました。

```tsx
declare namespace JSX {
  interface ElementChildrenAttribute {
    children: any;
  }
  type Element = {
    this_is_element: true;
  };
  interface IntrinsicElements {
    foo: {
      // fooの2つの属性を省略可能にした
      hoge?: string;
      fuga?: number;
      // childrenプロパティは文字列でも別の要素でもOK
      children: JSX.Element | string;
    };
    bar: {
      // barの子は何でもOK
      children?: unknown;
    };
  }
}

// 以下は全部OK
const elm = (
  <foo>
    <bar>文字列</bar>
  </foo>
);
const elm2 = (
  <foo hoge="文字列" fuga={123}>
    文字列
  </foo>
);
const elm3 = <bar>{456}</bar>;
const elm4 = <bar />;
```

### JSXと子要素の型の対応

これまで見たように、JSX内に出現した文字列は`string`型の子として扱われます。例えば`<bar>foobar</bar>`の場合`bar`要素の子として`string`型の値すなわち文字列が渡されます。

一方、`<bar>{456}</bar>`の場合は`bar`要素の子は`number`型でした。JSX内の`{}`という構文はその中身（通常のJavaScriptの式）をそのまま子として渡すという意味です。これにより任意のものを子とすることができます。

実は要素の子はもう1パターンあります。それは次の例のように子が複数ある場合です。

```tsx
const elm = (
  <foo>
    <bar>子1</bar>
    子2
    <bar />
  </foo>
);
```

この場合、`foo`に渡される`children`は**配列**となります。配列の要素はもちろんそれぞれの子要素です。

次のような定義にすることで、子として配列が渡された場合も対応することができますね。

```tsx
declare namespace JSX {
  interface ElementChildrenAttribute {
    children: any;
  }
  type Element = {
    this_is_element: true;
  };
  interface IntrinsicElements {
    foo: {
      hoge?: string;
      fuga?: number;
      // childrenプロパティに配列も許可
      children: JSX.Element | string | Array<JSX.Element | string>;
    };
    bar: {
      children?: unknown;
    };
  }
}

const elm = (
  <foo>
    <bar>子1</bar>
    子2
    <bar />
  </foo>
);
```

以上で、組み込み要素をどうやって定義するのかの話が終わりました。このように、`JSX.IntrinsicElements`インターフェースに要素を定義してやることで、どんな要素が存在し、それぞれがどんな属性を受け入れるのかを定義することができます。TypeScriptはそれに基づいて型チェックを行ってくれます。

## ユーザー定義コンポーネント

JSXでは組み込み要素のほかに、ユーザーが定義したコンポーネントを使用することができます。次はこれに対する型チェックを見ていきます。ユーザー定義コンポーネントは、JSX式では名前が大文字で始まるタグとして表れます。あらかじめタグ名と同名の変数に当該コンポーネントが入っている必要があります。以下はReactにおけるユーザー定義コンポーネントの例です。

```tsx:Reactでのユーザー定義コンポーネントの例
class MyComponent extends React.Component {
  render() {
    return <div>my component</div>;
  }
}
const elm = <MyComponent />;
```

ではTypeScriptとJSXの話に戻ります。前提として、デフォルトでは**関数**及び**クラス**がユーザー定義コンポーネントとして使用可能です。

```tsx
declare namespace JSX {
  interface ElementChildrenAttribute {
    children: any;
  }
  type Element = {
    this_is_element: true;
  };
  interface IntrinsicElements {
    bar: {
      children?: unknown;
    };
  }
}

class MyComponent1 {}
const MyComponent2 = () => <bar />;

const elm1 = <MyComponent1 />;
const elm2 = <MyComponent2 />;
```

## 関数コンポーネント

まず、ユーザー定義の関数コンポーネントから見ていきます。関数をコンポーネントとして使用する場合はひとつ条件があります。それは、**返り値の型が`JSX.Element`（に代入可能）でなければならない**点です。これに当てはまらない関数はコンポーネントとして使用できません。

```tsx
const NotComponent = () => 123;
// エラー（NotComponentの返り値がJSX.Elementではないので）
const elm3 = <NotComponent />;
```

次に、コンポーネントである以上、属性を受け取ることができます。関数コンポーネントが受け取れる属性はどのように決まるのでしょうか。これは単純で、**関数の引数の型が受け取れる属性一覧の型となります**。上の`MyComponent2`は引数が無かったので、何も属性を受け取りません。では、引数を受け取る関数コンポーネントを作ってみましょう。

```tsx
declare namespace JSX {
  interface ElementChildrenAttribute {
    children: any;
  }
  type Element = {
    this_is_element: true;
  };
  interface IntrinsicElements {
    bar: {
      children?: unknown;
    };
  }
}

// MyFunctionComponentの引数の型
interface MyFunctionComponentProps {
  foo: string;
  children?: JSX.Element | string | Array<JSX.Element | string>;
}
// MyFunctionの定義
const MyFunctionComponent = (props: MyFunctionComponentProps) => {
  return (
    <bar>
      {props.foo} {props.children}
    </bar>
  );
};
// 使用例
const elm = <MyFunctionComponent foo="123">child</MyFunctionComponent>;
```

`MyFunctionComponent`の定義に注目すると、引数の型が`MyFunctionComponentProps`であると宣言しています。これが受け取れる属性の一覧として採用されます。一覧の書き方は組み込み要素のときと同じです。今回の場合、`MyFunctionComponent`は文字列の`foo`属性を持ち、子を受け取ることも可能です。

## クラスコンポーネント

次にクラスコンポーネントです。先ほど見たように、デフォルトの状態では任意のクラスをクラスコンポーネントとして利用可能です。しかし、これは望ましくないことが多いでしょう。そこで、**どんなクラスがクラスコンポーネントとして利用可能かを制限することができます**。そのためには、**JSX.ElementClass**を定義します。その場合、**インスタンスの型が`JSX.ElementClass`に当てはまるクラス**がクラスコンポーネントとして利用可能になります[^legacy_class]。

[^legacy_class]: 今はあまり使われていないので詳細は省きますが、「`JSX.ElementClass`に当てはまるオブジェクトを返す関数」もクラスと見なされる仕様になっています。後述の関数コンポーネントとかなり紛らわしいですね。

```tsx
declare namespace JSX {
  interface ElementChildrenAttribute {
    children: any;
  }
  type Element = {
    this_is_element: true;
  };
  // ElementClassを定義
  interface ElementClass {
    render: () => any;
  }
  interface IntrinsicElements {
    bar: {
      children?: unknown;
    };
  }
}

class NotClassComponent {}
class ClassComponent {
  render() {
    return <bar />;
  }
}

// ClassComponentはクラスコンポーネントとして利用可能
const elm = <ClassComponent />;
// エラー（NotClassComponentはクラスコンポーネントではない）
const elm2 = <NotClassComponent />;
```

この例では、`JSX.ElementClass`は「`render`メソッドを持つオブジェクトの型」です。よって、インスタンスがその条件を満たすようなクラスがクラスコンポーネントとなります。実際、`NotClassComponent`のインスタンスは`render`メソッドを持ちませんが`ClassComponent`のインスタンスは`render`メソッドを持ちます。よって、`NotClassComponent`はクラスコンポーネントとして使用できない一方で`ClassComponent`はクラスコンポーネントとして使用可能です。

続いて、クラスコンポーネントの属性の指定方法です。これは2通りあり、1つ目は関数コンポーネントと似ています。クラスの**コンストラクタの引数の型**が受け取れる属性たちの型となります。

```tsx
declare namespace JSX {
  interface ElementChildrenAttribute {
    children: any;
  }
  type Element = {
    this_is_element: true;
  };
  interface ElementClass {
    render: () => any;
  }
  interface IntrinsicElements {
    bar: {
      children?: unknown;
    };
  }
}

// ClassComponentが受け取れる属性の型を定義
interface MyProps {
  hoge: string;
}
class ClassComponent {
  constructor(props: MyProps) {}
  render() {
    return <bar />;
  }
}

// ClassComponentはhoge属性を受け取る
const elm = <ClassComponent hoge="123" />;
```

もう一つの方法は、インスタンスの特定のプロパティの型を見よとする方法です。どのプロパティを見るべきかは`JSX.ElementAttributesProperty`インターフェースの唯一のプロパティとして宣言します。これは`JSX.ElementChildrenAttribute`を用いて子を表す属性を定義したのと似ていますね。

```tsx
declare namespace JSX {
  interface ElementChildrenAttribute {
    children: any;
  }
  // クラスコンポーネントの属性はインスタンスの`props`プロパティの型を見る
  interface ElementAttributesProperty {
    props: any;
  }
  type Element = {
    this_is_element: true;
  };
  interface ElementClass {
    render: () => any;
  }
  interface IntrinsicElements {
    bar: {
      children?: unknown;
    };
  }
}

interface MyProps {
  hoge: string;
}
class ClassComponent {
  // インスタンスのpropsプロパティの型が属性のプロパティとなる
  // （行儀が悪いけど今回は型のみ宣言）
  public props!: MyProps;
  render() {
    return <bar />;
  }
}

// ClassComponentはhoge属性を受け取る
const elm = <ClassComponent hoge="123" />;
```

以上でクラスコンポーネントの型チェックが分かりました。`JSX.ElementClass`を用いてクラスコンポーネントとして用いることができるクラスを制限し、クラスコンポーネントが受け取る属性は今回紹介した2種類の方法で指定します。

## 追加の属性を定義する

これまで見たように、属性の定義は組み込み要素・関数コンポーネント・クラスコンポーネントでそれぞれ異なっていました。実は、**コンポーネントに渡すことができる属性を定義する別の方法**が3種類あります。

### `JSX.IntrinsicAttributes`

1つ目は`JSX.IntrinsicAttributes`です。このインターフェースに定義された属性は、**全ての関数コンポーネント・クラスコンポーネントに対して適用されます**。例えばReactでは`key`属性が全てのコンポーネントで使用することができ、これに当てはまります[^react_key]。では例を見ましょう。

[^react_key]: Reactでは組み込み要素でも`key`属性が使用できますが、これは別途定義されており`JSX.IntrinsicAttributes`によるものではありません。

```tsx
declare namespace JSX {
  interface ElementChildrenAttribute {
    children: any;
  }
  interface ElementAttributesProperty {
    props: any;
  }
  type Element = {
    this_is_element: true;
  };
  interface ElementClass {
    render: () => any;
  }
  interface IntrinsicElements {
    bar: {
      children?: unknown;
    };
  }
  // 全ての要素に対して`key`属性を追加
  interface IntrinsicAttributes {
    key?: string;
  }
}

interface MyProps {
  hoge: string;
}
const MyComponent = ({ hoge }: MyProps) => <bar>{hoge}</bar>;
const elm = <MyComponent key="kkkk" hoge="123" />;
```

この例では、`MyComponent`に`key`属性を渡すことができています。`MyComponent`自身の定義に`key`はありませんから、これは`JSX.IntrinsicAttributes`のおかげです。なお、`key?: string;`というように`key`を省略可能なものとしている点に注意してください。別に絶対に省略可能である必要はありませんが、これを必須にすると全てのコンポーネントに`key`を渡す必要があり非常に面倒です。

### `JSX.IntrinsicClassAttributes<T>`

追加の属性を定義するもうひとつの方法は`JSX.IntrinsicClassAttributes<T>`です。その名前が示唆する通り、これは**クラスコンポーネント全てに対して適用される属性を表します**。関数コンポーネントに対しては適用されません。

これの特徴は、型引数`T`を取るようになっている点です。この`T`は当該のクラスコンポーネント自身のインスタンスの型です。つまり、例えば`MyComponent`がクラスコンポーネントであるとすると、このコンポーネントは自身が定義する属性に加えて`JSX.IntrinsicClassAttributes<MyComponent>`で得られる属性も持っていることになります。

あまりうまい例が思いつかないので例は省略しますが、Reactでは`ref`属性を定義するのにこれが使われています。

### `JSX.LibraryManagedAttributes<C, P>`

3つ目の`JSX.LibraryManagedAttributes<C, P>`は、一番新しくて複雑かつ強力な方法です。これは**関数コンポーネントとクラスコンポーネントの両方に適用されます**。型引数`C`はコンポーネント自体の型です。つまり、関数コンポーネントの場合は`C`は関数の型、クラスコンポーネントの場合は`C`はクラスの型です（インスタンスの型ではなくクラス自体の型である点に注意してください）。

型引数`P`がポイントで、これはそのコンポーネント自身により定義された属性たちの型です。この`JSX.LibraryManagedAttributes<C, P>`は`C`に応じて`P`を加工することができるのです。

それゆえ、この`JSX.LibraryManagedAttributes<C, P>`が定義されている場合、関数コンポーネント・クラスコンポーネントが取れる属性は`JSX.LibraryManagedAttributes<C, P>`で**置き換えられます**。追加ではありません。これがこの機能の強力なところです。

Reactではこれを使って`propTypes`と`defaultProps`をサポートしています。ここでは`defaultProps`を雑に実装してみます。

```tsx
declare namespace JSX {
  interface ElementChildrenAttribute {
    children: any;
  }
  type Element = {
    this_is_element: true;
  };
  interface ElementClass {
    render: () => any;
  }
  // コンポーネント向けの属性の加工を定義
  type LibraryManagedAttributes<C, P> = C extends { defaultProps: infer D }
    ? Partial<Pick<P, Extract<keyof P, keyof D>>> &
        Pick<P, Exclude<keyof P, keyof D>>
    : P;
}

interface MyProps {
  hoge: string;
  fuga: number;
}
class MyComponent {
  // hoge属性のデフォルト値を定義
  static defaultProps = {
    hoge: "default hoge"
  };
  constructor(props: MyProps) {}
  render() {}
}
// hoge属性が無くてもエラーにならない！
const elm = <MyComponent fuga={1234} />;
// hoge属性があってもOK
const elm2 = <MyComponent hoge="foobar" fuga={1234} />;
// fuga属性が無いのはエラー
const elm3 = <MyComponent hoge="hoge" />;
```

`JSX.LibraryManagedAttributes<C, P>`の定義がちょっと目が滑るかもしれませんが、とりあえず後回しにして結果を見ましょう。今回、`MyComponent`はコンストラクタの引数が`MyProps`なので、`hoge`と`fuga`という2つの属性を取るようになっています。どちらも省略可能ではありません。しかし、`const elm = ...`の例に見えるように`hoge`属性を省略してもエラーにはなりません。これは、`MyComponent`が取る属性が`JSX.LibraryManagedAttributes<C, P>`によって加工され、`hoge`が省略可能になったからです。

では、`JSX.LibraryManagedAttributes<C, P>`は何をしているのでしょうか。ここではconditional typesと、`Partial`, `Pick`, `Extract`, `Exclude`という組み込み型が使用されています。聞いたこともないという場合は[TypeScriptの型入門](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a)や[TypeScriptの型初級](https://qiita.com/uhyo/items/da21e2b3c10c8a03952f)を見るとよいでしょう（宣伝）。

ざっくり説明すると、`C extends { defaultProps: infer D }`というのは`C`が`defaultProps`プロパティを持つかどうかで分岐するという意味です。持っている場合は`defaultProps`の型が`D`に入ります。持っていない場合は`P`を加工せずにそのまま返します。`D`を取得したあとの部分を見ると、`&`の前、つまり`Partial<Pick<P, Extract<keyof P, keyof D>>>`は、`P`のプロパティのうち`D`に存在するものは省略可能にするという意味です、`&`の後、つまり`Pick<P, Exclude<keyof P, keyof D>>`は、`P`のプロパティのうち`D`に存在しないものはそのままにするという意味です。

今回の`MyComponent`に当てはめて考えると、まず`P`は`MyProps`、つまり`{ hoge: string; fuga: number; }`です。また、`C`は`typeof MyComponent`です。`MyComponent`は`defaultProps`というstaticプロパティを持つので、これの型が`D`に入ります。具体的には、`D`は`{ hoge: string; }`です。`P`のうち`D`に存在するもの、つまり`hoge`は省略可能になり、`D`に存在しないもの、つまり`fuga`はそのままですから、`JSX.LibraryManagedAttributes<C, P>`の結果は`{ hoge?: string; fuga: number; }`となります。

以上のように、`JSX.LibraryManagedAttributes<C, P>`を用いることで柔軟に属性を操作できます。また、実はこれがあれば`JSX.IntrinsicAttributes`や`JSX.IntrinsicClassAttributes<T>`は不要です。後者2つは後方互換性のために残されていますが、もし今から新しいライブラリを作って型定義を用意するとなれば`JSX.LibraryManagedAttributes<C, P>`だけで事足りることでしょう。

## `JSX`名前空間の位置について

これが最後の話題ですが、これまでこの記事全体を通して`JSX`名前空間を扱ってきました。これは明らかにグローバルに存在する名前空間です。ところがグローバルな空間を汚すのはあまり良くないということで、最近この仕様が変更され、ライブラリの名前空間の下に`JSX`名前空間を配置できるようになりました。

例えばReactの場合、`JSX`名前空間の代わりに`React.JSX`という名前空間を使用できます。こちらのほうがグローバルを汚さないので推奨されていますが、後方互換性の問題もありますからグローバルの`JSX`名前空間も引き続きサポートされます。

では、ライブラリの名前空間はどのように決まるのでしょうか。それはTypeScriptのコンパイルオプション`--jsxFactory`が関係しています。これはJSX記法を脱糖するときに使う関数を指定するオプションです（型にはあまり関係の無い話なので気になる方は自分で調べてみてください）。デフォルトではこれは`React.createElement`というメソッドです。このように`jsxFactory`がメソッドの場合、そのメソッドを有するオブジェクトがライブラリの名前空間として扱われます。

一方、`--jsxFactory h`のようにメソッドではなく単体の関数名を指定した場合、その関数自体が名前空間と見なされます。この場合は`h.JSX`という名前空間が参照されます。

では、最後にこれの例を見ましょう。実は`--jsxFactory h`の代わりにファイル中に`/* @jsx h */`というコメントを入れてもOKなので今回はそれで対応しています。

```tsx
/* @jsx h */
declare namespace h.JSX {
  interface ElementChildrenAttribute {
    children: any;
  }
  type Element = {
    this_is_element: true;
  };
  interface ElementClass {
    render: () => any;
  }
  type LibraryManagedAttributes<C, P> = C extends { defaultProps: infer D }
    ? Partial<Pick<P, Extract<keyof P, keyof D>>> &
        Pick<P, Exclude<keyof P, keyof D>>
    : P;
}

interface MyProps {
  hoge: string;
  fuga: number;
}
class MyComponent {
  // hoge属性のデフォルト値を定義
  static defaultProps = {
    hoge: "default hoge"
  };
  constructor(props: MyProps) {}
  render() {}
}
// hoge属性が無くてもエラーにならない！
const elm = <MyComponent fuga={1234} />;
// hoge属性があってもOK
const elm2 = <MyComponent hoge="foobar" fuga={1234} />;
```

この例では、名前空間をグローバルの`JSX`ではなく`h.JSX`にしましたが、引き続きちゃんと動作しています。試しに`h.JSX`を`foobar.JSX`など違うものにしてみると、この名前空間は認識されなくなり上のコードはエラーとなります。

このように`JSX`名前空間をライブラリの名前空間の下に置くことができる機能は、[複数のJSX対応ライブラリを同時に使用したいというなかなか壮絶な要望](https://github.com/Microsoft/TypeScript/issues/18131)を受けて対応されたものらしいです。

## まとめ

この記事ではTypeScriptに組み込みの`JSX`名前空間を通してJSXの型チェックを操作する方法を解説しました。TypeScriptにおけるJSXの型チェックは、要素にどのような属性を渡すことができるのかという点に特に焦点が当てられています。要素は組み込み要素、関数コンポーネント、クラスコンポーネントの3種類があり、受け入れられる属性の定義方法はそれぞれで異なっています。また、属性を一括で操作する方法も提供されています。

一応Reactと癒着しすぎずに汎用的な仕組みを提供しようとしているようですが、Reactを使っている方は何となくお察しのとおり、かなりReactに引きずられているように見えます。

ともかく、これでTypeScriptがJSXを使うライブラリ向けにどのようなサポートを提供しているのか分かりましたから、次のステップとしてはReactの型定義、つまり`@types/react`を読んでみるのも良いかもしれません。`JSX`名前空間を取っ掛かりとすることでかなり読解できるはずです。また、もし自分でJSXをサポートするライブラリを作りたくなったらこの記事を思い出して型定義を書いてみましょう。