useStateの型

useStateの状態管理関数をPropsで渡すとき、受け取る側は

Dispatch<SetStateAction<T>>;

という方で受け取ることになる。

ジェネリクスのメリットがいまいち

関係ないけどずっと気になっていた「ジェネリクスのメリットがいまいちピンとこない件」について、ついでに調べてみることにした。

例えば、

// 文字列を入力し、文字列を出力する関数
const returnText = (input: string): string => {
  return input;
};

// 数値を入力し、数値を出力する関数
const returnNumber = (input: number): number => {
  return input;
};

console.log(returnText("hello world"), "returnText");
console.log(returnNumber(0), "returnNumber");

という関数が存在した時、

const returnTextOrNumber = (input: string | number): string | number => {
  return input;
};

console.log(returnTextOrNumber("hello world"), "returnTextOrNumber");
console.log(returnTextOrNumber(0), "returnTextOrNumber");

こんな感じにまとめたくなる。

これをジェネリック型にすると、

const returnTextOrNumberGeneric = <T extends string | number>(input: T): T => {
  return input;
};

こんな感じになる。

型ガードが必要

同じように感じるけど、returnTextOrNumberの場合は、

const a: string | number = returnTextOrNumber("hello world");
const b: string | number = returnTextOrNumber(0);

console.log(a.length);
// Property 'length' does not exist on type 'string | number'.
// Property 'length' does not exist on type 'number'

console.log(b + 1);
// Operator '+' cannot be applied to types 'string | number' and 'number'.

stringnumberのどちらが返ってくる確定していないので、コンパイルエラーが出る。

実行するためには

if (typeof a === "string") {
  console.log(a.length);
}
if (typeof b === "number") {
  console.log(b + 1);
}

のように型ガードする必要がある。

コンパイラーをだませる

const b: string | number = returnTextOrNumber(0) as string;
console.log(b.length);

bは0なのでlengthが存在しないが、as stringで強制的に文字列とコンパイラーと認識させられる。

これはコード的負の遺産になる。

ジェネリック型は堅牢でコード量が少ない

対照的にジェネリック型であるreturnTextOrNumberGeneric の場合は、

const a = returnTextOrNumberGeneric<string>("hello world");
const b = returnTextOrNumberGeneric<number>(0);

console.log(a.length);
console.log(b + 1);

関数実行時に入出力共に型を指定し確定できるので、コンパイルエラーがでない。

また

const b = returnTextOrNumberGeneric<number>(0) as string;
// Conversion of type 'number' to type 'string' may be a mistake because neither type sufficiently overlaps with the other. If this // was intentional, convert the expression to 'unknown' first.(2352)

強制的に異なる方でコンパイラーを納得させようとしても不可能になり、堅牢制が向上した。

実際に整理してみるとジェネリック型の恩恵がよくわかった。