ネイティブのJavaScriptでJSXを使ってみる

皆さん、こんにちは。技術開発グループのn-ozawanです。
寒さも和らぎ暖かくなってきましたね。今年の花粉飛散量は例年よりも多いとのことです。

本題です。
先日、ReactなどのSPAフレームワークを使わず、TypeScriptのみでDOM要素を実装している現場がありました。DOM要素をHTMLで記述出来ればいいのですが、TypeScriptだとどうしても可読性が低くなりますね。何とか解決できないものかと調べてみたところ、ReactライブラリがなくともJSXが使えることが分かったので紹介します。

JSX

JSXとは

JSXは、JavaScript言語構文の拡張機能で、主にReactで利用されています。
JSXに関してはネットで検索してもらえればいくらでも情報は出てきますし、React公式ページでも詳細に紹介されています。簡単に説明すると、以下のようにJavaScript(ないしはTypeScript)の構文中に、XMLライクな構文を記述することが出来る、というものです。

const element = <h1 class="greeting">Hello, world!</h1>

Reactで採用されていることからも分かる通り、JSXの仕様を策定したのはFacebook社です。

TypeScriptとJSX

さて、このJSXですが、先にも述べた通り主に利用されるのはReactです。ですが、TypeScript1.6からReactからのフィードバックを受けてJSXがサポートされており、.tsxという拡張子であればコンパイルしてくれるようになりました。つまり、Reactが動作しないネイティブなJavaScript環境でもJSXが利用できる、ということになります。

JSXをコンパイルしてみる

では実際にtscコマンドで.tsxファイルをコンパイルしてみましょう。
まず、tsconfig.jsonの”compilerOptions”に、”jsx”: “react”を追加します。

{
  "compilerOptions": {
    "jsx": "react",
  }
}

次に.tsxファイルを用意します。

function view() {
  const element = <h1 class="greeting">Hello, world!</h1>
}

これをコンパイルするとsample.jsファイルが作成されます。

function view() {
  const element = React.createElement("h1", { class: "greeting" }, "Hello, world!");
}

JSXで記述されていたところが、React.createElement()関数に置き換わりました。しかしこれではReact.createElement()関数をImportする必要があります。表題にある通り、Reactがないネイティブな環境でJSXを利用したい場合はどうすればいいのでしょうか?

jsxFactory

JSXの内部処理を指定したいときはjsxFactoryを利用します。実際にやってみましょう。
tsconfig.jsonの”compilerOptions”に、”jsxFactory”: “createElement”を追加します。

{
  "compilerOptions": {
    "jsx": "react",
    "jsxFactory": "createElement",
  }
}

次にjsxFactoryで指定したcreateElement関数を用意します。

function createElement(element: string, props: {[key: string]: string}, ...children: any[]): Element {
    // 要素を作成
    const elem: Element = document.createElement(element);

    // 属性を設定
    Object.keys(props || {}).forEach(x => elem.setAttribute(x, props[x]));

    // 子要素を追加
    elem.append(...children);

    // 返却
    return elem;
}

function view() {
  const element = <h1 class="greeting">Hello, world!</h1>
}

やっていることはかなりシンプルです。要素を作成して、属性を設定して、子要素を追加しているだけです。今回は紹介ということで、型も適当ですし、いくつか処理を省略しています。

さて、これをコンパイルするとsample.jsファイルが作成されます。React.createElement()関数ではなく、今回作成したcreateElement()関数に置き換わっていることが分かります。

function createElement(element: string, props: {[key: string]: string}, ...children: any[]): Element {
    // 要素を作成
    const elem: Element = document.createElement(element);

    // 属性を設定
    Object.keys(props || {}).forEach(x => elem.setAttribute(x, props[x]));

    // 子要素を追加
    elem.append(...children);

    // 返却
    return elem;
}

function view() {
  const element = createElement("h1", { class: "greeting" }, "Hello, world!");
}

おわりに

ネイティブなJavaScript環境でもJSXが利用できることがなんとく分かったかと思います。今回は紹介ということで、かなり大事なことを省略しています。実際に上記のコードをコンパイルすると、JavaScriptファイルは出力されるものの、型に関するエラーが出力されます。本当はJSX.IntrinsicElementsを定義する必要があるためです。

他にはFlagmentやFunctionComponentなども考慮する必要がありますね。

TypeScriptのJSXに関しては、TypeScriptハンドブック-JSXに詳細が書かれていますので、そちらを一読ください。また、型定義については@type/react のソースコードが参考になりますのでそちらもお勧めです。

採用情報

「チームアイオス」入団者募集

〜就活で悩むアナタに来て欲しい〜