TypeScriptのJSX namespace
JSX構文に対して適用される型の決まり方
グローバルなJSXnamespace あるいは、JSXファクトリ関数に付随するJSXnamespaceによって決まる グローバルなJSX namespaceは↓のように宣言されるもの
code: ts
declare global {
namespace JSX {
interface IntrinsicElements {
// ...
}
}
}
大昔はglobalに定義するしか無かったが、今は後方互換を除いてこうする必要は無いはず
恐らくTypeScript2.8以前?
JSXファクトリ関数に付随するJSXnamespaceとは
code:tsx
import React from 'react';
export const HelloWorld = () => <h1>Hello world</h1>;
↑が↓のように変換されるとすると
code: ts
import React from 'react';
export const HelloWorld = () => React.createElement("h1", null, "Hello world");
code:ts
declare namespace React {
namespace JSX {
interface IntrinsicElements {
// ...
}
}
}
のようにして宣言される名前空間がそれに当たる
TypeScriptのjsxコンパイラオプションによってJSXファクトリ関数として解決される関数は異なる
jsx: "react"の場合はjsxFactoryオプションが影響する
jsxFactoryオプションはJSXのファクトリ関数、つまりReact.createElementの部分を指定するもの
例えばpreactの場合はpreact.hと指定するはず
デフォルト値はReact.createElementになっている
他の値にした場合は、その値がJSX名前空間の解決に用いられる
jsxFactory: "preact.h"とした場合、↓が対応する
code: ts
declare namespace preact {
namespace JSX {
interface IntrinsicElements {
// ...
}
}
}
モジュールからnamespaceをexportしても同じになるはず
code: preact/index.d.ts
export namespace JSX {
interface IntrinsicElements {
// ...
}
}
また、jsxFactory: "h"のように関数単体を指定した場合は、関数が名前空間と見なされる
code: ts
declare namespace h.JSX {
interface IntrinsicElements {
// ...
}
}
jsx: "react-jsx"、jsx: "react-jsxdev"の場合
code: tsx
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
export const HelloWorld = () => _jsx("h1", { children: "Hello world" }, void 0);
のように変換されるので、react/jsx-runtimeモジュールからJSX namespaceをexportすれば良いはず
ちなみにreact/jsx-runtimeのreactの部分はjsxImportSourceオプションで指定できる
JSX namespace内のinterface
IntrinsicElements
組み込み要素に対する属性の型を定義する
code: ts
interface IntrinsicElements {
foo: {
bar: number;
}
}
Element
JSXの返り値の型を定義する
要素ごとに返り値の型を変えることはできない
code: ts
type Element = {
jsxProviderInternalProps: unknown;
}
const hogeElement = (<hoge />) satisfies Element;
IntrinsicAttributes
全てのコンポーネントの属性に追加される型を定義する
reactのkeyなどがこれに当たる
code:ts
interface IntrinsicAttributes {
key?: string;
}
ElementChildrenAttribute
JSXの子として扱うpropのkeyと値の型を定義する
code: ts
interface ElementChildrenAttribute {
children: any
}
こう書いた場合、IntrinsicElementsで要素ごとにchildrenの型を指定できる
children以外にすることもでき、そうするとpropsで子要素の型を指定する際のkeyが変わる
LibraryManagedAttributes<C, P>
全てのコンポーネントの属性を定義をラップする型を定義する
IntrinsicAttributesの上位互換
型引数Cは関数(あるいはクラス)コンポーネント自体の型変数で、型引数Pは属性の型変数になる
code:ts
type LibraryManagedAttributes<C, P> = P & {
key?: string;
}
こう書くと、全てのコンポーネントの属性にoptionalなkeyが追加される
ElementClass
クラスコンポーネントの型を定義する
この型に当てはまる型(extends ElementClass?)のclassがクラスコンポーネントとして扱われる
ElementAttributesProperty
クラスコンポーネントの属性として扱うプロパティを定義する
IntrinsicClassAttributes<T>
クラスコンポーネント全てに追加される属性の型を定義する
型引数Tはクラスコンポーネント自体の型
参考