かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

React カスタム hook で返すデフォルト引数のある関数の型をいい感じに書きたい。

React hooks では custom hooks を多用するのですが、custom hook で返す関数がデフォルト引数を取る時の型をいい感じに書きたかったのメモ。

デフォルト引数を取る関数を返す Custom Hook

こんな Hooks の型をいい感じに書きたい

export const useCount = () => {
  const [count, setCount] = useState(0); 
  
  const update = useCallback(( x = 1 ) => {
    setCount((oldCount) => oldCount + x);
  }, []);

  return {
    count,
    update,
  }
};

interface と関数とでそれぞれ関数の型を作ると、異なる型定義になってイケてない

interface に直接返される関数を定義すると微妙になってしまう

 interface IUseCount {
  count: number;
  update: (x?: number) => void;
}

export const useCount = (): IUseCount => {
  const [count, setCount] = useState<boolean>(0);
  
  const update = useCallback(( x: number | undefined = 1 ): void => {
    setCount((oldCount) => oldCount + x);
  }, []);

  return {
    count,
    update,
  }
};

update はデフォルト引数なので、関数の定義部分は (x?: number) => void となる。
実際の関数の部分はデフォルト引数を持たせるので、 (x?: number = 1): void => {...} のように書きたいがこう書くと Parameter cannot have question mark and initializer. というエラーになってしまうので、undefined を許容できるように ( x: number | undefined = 1 ): void => {...} と定義すればデフォルト引数のある関数として動作させられる。

動き上は問題がないのだけど、hook の返す型としている interface での関数の型定義と、hooks 内の関数に定義している型とが異なるのでイケてない感じです。

関数の型を別途定義すると型が揃っていい感じになる

関数の型を取り出して別途定義すると interface と hooks 内の関数の型定義を同じにすることができました!

type updateType = (x?: number) => void;

interface IUseCount {
  count: number;
  update: updateType;
}

export const useCount = (): IUseCount => {
  const [count, setCount] = useState<boolean>(0);
  
  const update = useCallback<updateType>(( x = 1 ) => {
    setCount((oldCount) => oldCount + x);
  }, []);

  return {
    count,
    update,
  }
};

関数の型を別途作成して interface, hooks 内の関数に当てはめることで、推論される型も同じになっていい感じになりました!

今まで同じ型なのにそれぞれに型書くのなんかイケてないな〜と感じつつ、interface 内に直接関数の型を書いて、関数には引数と戻り値の型を直接書いていたのですが、関数の方だけ作っておいて使い回せばよかったのですね!なっとく!


[参考]