724 文字
4 分
Reactのpropsをunion typeにする方法について
Union types of props
複雑なサービスを作っていると、Union types を 1 つのメソッドで処理したくなることがあるかもしれない。しかし安直に書いてしまうと上手くいかない。このような型定義にライブラリで出会ってしまうと最悪な気持ちになる。
export type Props = | { text: Item; } | { multilineText: Item; } | { select: Item; };
export const SomeComponent = (props: Props) => { return ( <div> {props?.text ? ( <div>{props?.text.value}</div> ) : props?.multilineText ? ( <div>{props?.multilineText.value}</div> ) : props?.select ? ( <div>{props?.select.value}</div> ) : null} </div> );};
Kind pattern
各型に共通のプロパティを持たせ、それぞれの値を固有の Literal type などにするパターン。
このプロパティを比較することで、どの型を利用するかの識別が可能になる。
export type Props = | { kind: "text"; text: Item; } | { kind: "multilineText"; multilineText: Item; } | { kind: "select"; select: Item; };
const SomeComponent = (props: Props) => { return ( <div> {props?.kind === "text" ? ( <div>{props?.text.value}</div> ) : props?.kind === "multilineText" ? ( <div>{props?.multilineText.value}</div> ) : props?.kind === "select" ? ( <div>{props?.select.value}</div> ) : null} </div> );};
Optional never Trick
次のように、擬似的に全ての型へ同様のプロパティを持たせることで対応できる。
このパターンの場合、レスポンスなどで受け取ったオブジェクトを編集しなくていい場合があるというメリットと、プロパティを列挙しなければならないというデメリットがある。
export type Props = | { text: Item; multilineText?: never; select?: never; } | { text?: never; multilineText: Item; select?: never; } | { text?: never; multilineText?: never; select: Item; } | { text?: never; multilineText?: never; select?: never; };
const SomeComponent = (props: Props) => { return ( <div> {props?.text ? ( <div>{props?.text.value}</div> ) : props?.multilineText ? ( <div>{props?.multilineText.value}</div> ) : props?.select ? ( <div>{props?.select.value}</div> ) : null} </div> );};
次のような Utility type を生やしておけば、デメリットを解消できる。
type XOR< T extends Record<string, unknown>, U extends string = T extends T ? keyof T : never> = T extends T ? { [k in keyof T]: T[k]; } & { [k in Exclude<U, keyof T>]?: never; } : never;
export type Props = XOR< | { text: Item; } | { multilineText: Item; } | { select: Item; }>;
export const SomeComponent = (props: Props) => { return ( <div> {props?.text ? ( <div>{props?.text.value}</div> ) : props?.multilineText ? ( <div>{props?.multilineText.value}</div> ) : props?.select ? ( <div>{props?.select.value}</div> ) : null} </div> );};
Utility types of TypeScript
https://www.typescriptlang.org/docs/handbook/utility-types.html
TypeScript には便利なユーティリティタイプがいくつか同梱されている。
個人的に最低限知っておいた方がいいと思うものを列挙する。
- Required
Reactのpropsをunion typeにする方法について
https://blog.ohirunewani.com/posts/react-props-union-type/