TypeScriptのジェネリクスってなあに

type Props = {
  title: string;
  children: ReactNode | undefined;
}
const Heading = ( { title, children }:Props ) => {
  return (
    <>
      <h1>{ title }</h1>
      <div>{ children }</div>
    </>
  );
}

こんな感じでコンポーネントを書いていたところ、PropsWithChildren を使えばいいじゃん、と教えてもらうなどしました。で、ジェネリクスについて掘り下げて教えてもらったので備忘録。

type Props = PropsWithChildren<{ title: string }>
// type Props = { title: string } & { children?: ReactNode | undefined } と同義
const Heading = ( { title, children }:Props ) => {
  return (
    <>
      <h1>{ title }</h1>
      <div>{ children }</div>
    </>
  );
}

https://typescript-jp.gitbook.io/deep-dive/type-system/generics

https://js.studio-kingdom.com/typescript/handbook/generics

https://qiita.com/k-penguin-sato/items/9baa959e8919157afcd4

ジェネリクスは一言で言うと外から設定できる型定義のこと。

@types/react にはいろいろな便利な型が用意されてるので、自前で時間かけて書くよりもともとあるものを使ったほうが時短になってよい。(そういう型があるってのはコード見ないとわからないけども………)

PropsWithChildrenだけじゃなく、大体の関数にジェネリクスは定義されている。

おなじみ useStateもジェネリクスがあるので、こういうことができる。

// Todoリストの状態管理
const [state, setState] = useState<'ready' | 'inprogress' | 'completed' | ''>('');

//  レビューの星管理
const [reviewStar, setReviewStar] = useState<1 | 2 | 3 | 4 | 5>(1);

useState自体、特に型定義しなくても引数の中身を見て推察してくれるけど、中身を絞りたいときにはとても有用。

もしくは、初期値をnullにした場合、nullしか受け付けてくれなくなるので、ジェネリクスに ReactNode をいれるなど。

// これだと setName で nullしか入らなくなる
const [name, setName] = useState(null);

// ジェネリクスにReactNodeを入れておくと、型がReactNodeになるのでnull以外も設定できる
const [name, setName] = useState<ReactNode>(null);

ジェネリクスは沼らしいので細かく設定しすぎたり時間かけ過ぎたりするのは本末転倒だから気をつける!