次のコンポーネントがあります。
export enum Tags {
button = 'button',
a = 'a',
input = 'input',
}
type ButtonProps = {
tag: Tags.button;
} & ({ a?: string; b?: undefined } | { a?: undefined; b?: string }) &
JSX.IntrinsicElements['button'];
type AnchorProps = {
tag: Tags.a;
} & ({ a?: string; b?: undefined } | { a?: undefined; b?: string }) &
JSX.IntrinsicElements['a'];
type InputProps = {
tag: Tags.input;
} & ({ a?: string; b?: undefined } | { a?: undefined; b?: string }) &
JSX.IntrinsicElements['input'];
type Props = ButtonProps | AnchorProps | InputProps;
const Button: React.FC<Props> = ({ children, tag }) => {
if (tag === Tags.button) {
return <button>{children}</button>;
}
if (tag === Tags.a) {
return <a href="#">{children}</a>;
}
if (tag === Tags.input) {
return <input type="button" />;
}
return null;
};
// In this instance the `href` should create a TS error but doesn't...
<Button tag={Tags.button} href="#">Click me</Button>
// ... however this does
<Button tag={Tags.button} href="#" a="foo">Click me</Button>
これは、この質問をすることができるように少し取り除かれています。重要なのは、私が交差点タイプとともに差別的連合を試みているということです。タグの値に基づいて、目的の小道具を達成しようとしています。したがって、Tags.button
が使用され、次にJSXのボタン属性が使用されます(上記の例ではhref
はbutton
要素では許可されていないため、エラーが発生するはずです)。 a
またはb
を使用しますが、一緒に使用することはできません-したがって、交差タイプです。
ここで何が問題になっていますか?また、a
またはb
プロパティを追加したときに型が期待どおりに機能するのはなぜですか?
いつエラーが発生し、いつコンパイルする必要があるかを示すために、例付きのプレイグラウンドを追加しました。
通常、私は次の手法を使用して一般的な機能コンポーネントを作成しますが、それはあなたのケースでも機能します。トリックは、コンポーネントをfunction
ではなくconst
として宣言して、汎用的にできるようにすることです。これにより、プロップを一般的にすることができ、次のことが可能になります。
export enum Tags {
button = 'button',
a = 'a',
input = 'input',
}
type Props<T extends Tags = Tags> = JSX.IntrinsicElements[T] & {
tag: T;
} & ({ a?: string; b?: never } | { a?: never; b?: string });
function Button<T extends Tags>({ children, tag }: Props<T>) {
if (tag === Tags.button) {
return <button>{children}</button>;
}
if (tag === Tags.a) {
return <a href="#">{children}</a>;
}
if (tag === Tags.input) {
return <input type="button" />;
}
return null;
}
// These should error due to href not being allowed on a button element
const a = <Button tag={Tags.button} href="#" a="foo">Click me</Button>
const b = <Button tag={Tags.button} href="#">Click me</Button>
// These should work
const c = <Button tag={Tags.button} a="foo">Click me</Button>
const d = <Button tag={Tags.button} b="foo">Click me</Button>
const e = <Button tag={Tags.button}>Click me</Button>
// This should error as `a` and `b` can't be used together
const f = <Button tag={Tags.button} a="#" b='a'>Click me</Button>
唯一の欠点は、Button
コンポーネントを直接入力できないこと、function Button<T>
がReact.FC<Props<T>>
であるとは言えないこと、その小道具と戻り値の型のみを入力できることです。
遊び場はこちら を確認してください(例は一番下に残しました)
[〜#〜] edit [〜#〜]質問の例を使用してコードを更新し、遊び場のリンクを修正しました