React×Typescript でよくあるのが 「同じ Props の型宣言を何度も書くのが面倒くさい!」 ということ。
しかし ComponentProps
を使用すれば子コンポーネントの Props の型を参照できるので、簡潔に記述することができます。
ComponentProps を使ってみる
全部の型を参照
例えば以下のような画像を表示するだけの処理があるとします。
import React, { FC } from 'react';
type Props = {
image: {
id: number;
url: string;
};
};
export const Item: FC<Props> = ({ image }) => (
<img src={image.url} alt={String(image.id)} />
);
このコンポーネントを呼び出す場合、親にも同じ型宣言が必要になります。
import React, { ComponentProps } from 'react';
import { Item } from 'Item';
type Props = {
image: {
id: number;
url: string;
};
};
export const List = ({ image }: Props) => (
<Item image={image} />
)
面倒ですね。
そこで登場するのが ComponentProps
。
ComponentProps
を使えば、上記 List.tsx
の Props 内を ComponentProps
で置き換えることができます。
import React from 'react';
import { Item } from 'Item';
type Props = React.ComponentProps<typeof Item>;
export const List = ({ image }: Props) => (
<Item image={image} />
)
ComponentProps<typeof [コンポーネント]>
と指定することで、対象コンポーネントに指定されている型を参照することができます。
今回の場合は Item.tsx
で export const Item: FC<Props> = ...
とジェネリクスに Props が指定されているので、Props を参照していることになります。
一部の型を参照
型を一部だけ参照することも可能です。
例えば下記 Props の中身から image だけ参照したい場合もあります。
type Props = {
image: {
id: number;
url: string;
};
user: {
id: number;
name: string;
};
};
その場合は、ComponentProps<typeof Item>
の後にさらに参照したい部分だけ記述するといいでしょう。
type Props = {
image: React.ComponentProps<typeof Item>['image'];
};
型の拡張・縮小を行う
ComponentProps
で型の拡張・縮小も楽に行うことができます。
が、その前にまずは Typescript における type の操作方法について見ていきましょう。
Type の操作方法
型の結合
型をそのまま結合します。
type A = {
a1: string;
a2: number;
};
type B = {
b1: number;
b2: string;
}
type AB = A & B;
// ↑の内容
type AB = {
a1: string;
a2: number;
b1: number;
b2: string;
}
型の抜き出し
指定した型のみで構築します。
type ABPick = Pick<AB, 'a1' | 'b1'>;
// ↑の内容
type ABPick = {
a1: string;
b1: number;
}
型の削除
指定した型を削除して構築します。
type ABOmit = Omit<AB, 'a1' | 'b1'>;
// ↑の内容
type ABPick = {
a2: number;
b2: string;
}
よく使いそうな物を紹介しました。
他にも Partial など沢山あるので、詳しくはこちらを参照してください。
https://www.typescriptlang.org/docs/handbook/utility-types.html
ComponentProps の利用
それでは ComponentProps を利用します。
まずは上記 Item.tsx
に要素を追加したくなったので、下記のように User
コンポーネントを追加したとしましょう。
import React, { FC } from 'react';
import { User } from 'User';
type Props = {
image: {
id: number;
url: string;
};
user: {
id: number;
name: string;
};
};
export const Item: FC<Props> = ({ image, user }) => (
<>
<img src={image.url} alt={String(image.id)} />
<User user={user} />
</>
);
ここで User の型宣言を ComponentProps に変えて、
type の型結合を行えば下記のように すっきりした記述になります。
import React, { FC } from 'react';
import { User } from 'User';
// ComponentProps を利用する
type UserProps = React.ComponentProps<typeof User>;
type Props = {
image: {
id: number;
url: string;
};
} & UserProps; // UserProps を Props と結合する
export const Item: FC<Props> = ({ image, user }) => (
<>
<img src={image.url} alt={String(image.id)} />
<User user={user} />
</>
);
もし User の型宣言が
type Props = {
user: {
id: number;
name: string;
};
avatar: {
url: string;
}
};
のようになっていて、user 部分だけ結合したいという場合は
Props 部分は下記のようになるでしょう。
type Props = {
image: {
id: number;
url: string;
};
} & Pick<UserProps, 'user'>;
まとめ
ComponentProps を利用すればかなり快適に Props の受け渡しが楽になるのでおすすめです。