kk-web

結局 React のコンポーネントにはどうやって型をつければ良いのさ

2020-10-23

React TypeScript Cheatsheet の Useful Patterns by Use Case の Wrapping/Mirroring に答えが書かれていました。

自分は function でなく const で作成することが多い(どっちが良いんですかね?)ので、以下のように書くのが良さそうです。

import React, { ComponentPropsWithoutRef, FC } from "react";

export type ButtonProps = ComponentPropsWithoutRef<"button"> & {
  specialProp?: string;
};

const Button: FC<ButtonProps> = ({ specialProp, ...props }: ButtonProps) => {
  console.log(specialProp);

  return <button {...props} />;
};

export default Button;

確かに見通し良いですね、自分も今後はこう書いていこうかなーと思います。


蛇足

上の記事を読んでいくと、Why not ComponentProps or IntrinsicElements or [Element]HTMLAttributes or HTMLProps or HTMLAttributes? って欄が出てきます。

ComponentProps はイマイチ

ComponentPropsref が含まれているのか含まれていないのかわかりづらいですよね。

そのため ComponentProps は使用せず、ComponentPropsWithoutRefComponentPropsWithRef を使うほうが良さそうです。

IntrinsicElements or [Element]HTMLAttributes はまぁ…

以下のように書いても正常動作します。

import React, { FC } from "react";

export type ButtonProps = JSX.IntrinsicElements["button"] & {
  specialProp?: string;
};
import React, { ButtonHTMLAttributes, FC } from "react";

export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
  specialProp?: string;
};

Looking at the source for ComponentProps shows that this is a clever wrapper for JSX.IntrinsicElements, whereas the second method relies on specialized interfaces with unfamiliar naming/capitalization.

ComponentProps のソースを見ると、これが JSX.IntrinsicElements の巧妙なラッパーであることがわかりますが、2 番目の方法は、なじみのない名前付け/大文字の使用を伴う特殊なインターフェイスに依存しています。

Ultimately, we picked the ComponentProps method as it involves the least TS specific jargon and has the most ease of use. But you'll be fine with either of these methods if you prefer.

最終的に、TS 固有の専門用語が最も少なく、使いやすさが最も高い ComponentProps メソッドを選択しました。 ただし、必要に応じて、これらの方法のいずれでも問題ありません。

とのことなので IntrinsicElements でも [Element]HTMLAttributes を使用しても問題はなさそうです。

ただ ComponentPropsWithoutRef のラッパーとは書かれていないので、最も良い書き方ではないかもしれないです。

React.HTMLProps or React.HTMLAttributes はダメ

import React, { FC, HTMLProps } from "react";

export type ButtonProps = HTMLProps<HTMLButtonElement> & {
  specialProp?: string;
};

const Button: FC<ButtonProps> = ({ specialProp, ...props }: ButtonProps) => {
  console.log(specialProp);

  // error: 型 'string' を型 '"button" | "submit" | "reset"' に割り当てることはできません。ts(2322)
  return <button {...props} />;
};

export default Button;

これはわかりやすくダメですね。

HTMLPropsAllHTMLAttributes を継承しているので、特定のタグで使用すべきではありません。

import React, { FC, HTMLAttributes } from "react";

export type ButtonProps = HTMLAttributes<HTMLButtonElement> & {
  specialProp?: string;
};

こっちは呼ぶ側でエラーが起きます、これも汎用的な型なんですかね?

© 2018 kk-web