同じ種類の関数は返り値の型を揃える
予測可能性
API を叩くのと関連した Hook のように同じ種類の関数やHookがお互い違うタイプの返り値を持っていると、コードの一貫性が損なわれ、一緒に働くチームメンバーがコードを読むのが困難になります。
📝 コード例 1: useUser
次のuseUserとuseServerTimeHookはすべてAPIを叩くのと関連したHookです。
しかしuseUserは@tanstack/react-queryのQueryオブジェクトを返し、useServerTimeはサーバー時間を持ってきてデータだけを返しています。
typescript
import { useQuery } from "@tanstack/react-query";
function useUser() {
const query = useQuery({
queryKey: ["user"],
queryFn: () => fetchUser()
});
return query;
}
function useServerTime() {
const query = useQuery({
queryKey: ["serverTime"],
queryFn: () => fetchServerTime()
});
return query.data;
}👃 コードの不吉な臭いを嗅いでみる
予測可能性
サーバーのAPIを叩くHookの返り値のタイプがお互いに違うと、チームメンバーはこのようなHookを使うたびに返り値が何なのか確認しないといけません。Queryオブジェクトを返すと、dataを取り出す必要があり、データだけを返してあげればそのまま値を使えますよね。
同じように動くコードが一貫性を持って規則的でないとコードを読むのが難しくなります。
✏️ リファクタリングしてみる
次のようにサーバーのAPIを叩くHookは一貫性を持たせてQueryオブジェクトを返してあげるようにすれば、チームメンバーがコードを予測できる可能性が高まります。
typescript
import { useQuery } from "@tanstack/react-query";
function useUser() {
const query = useQuery({
queryKey: ["user"],
queryFn: () => fetchUser()
});
return query;
}
function useServerTime() {
const query = useQuery({
queryKey: ["serverTime"],
queryFn: () => fetchServerTime()
});
return query;
}📝 コード例 2: checkIsValid
次のcheckIsNameValidとcheckIsAgeValidはすべての名前と年齢が正しいか検証する関数です。
typescript
/** ユーザーネームは20字未満でないといけません。 */
function checkIsNameValid(name: string) {
const isValid = name.length > 0 && name.length < 20;
return isValid;
}
/** ユーザーは年齢が18歳以上99歳次の自然数でないといけません。 */
function checkIsAgeValid(age: number) {
if (!Number.isInteger(age)) {
return {
ok: false,
reason: "年齢は整数でないといけません。"
};
}
if (age < 18) {
return {
ok: false,
reason: "年齢は18歳以上でないといけません。"
};
}
if (age > 99) {
return {
ok: false,
reason: "年齢は99歳以下でないといけません。"
};
}
return { ok: true };
}👃 コードの不吉な臭いを嗅いでみる
予測可能性
検証関数の返り値が違うと、チームメンバーは関数を使うたびに返り値を確認する必要があり、コードリーディングが難しくなってしまいます。
特に厳密なブールの比較のような機能を使わない場合、コードにバグが見つかる原因になっていまいます。
typescript
// このコードは名前が規則を守っているか検証します。
if (checkIsNameValid(name)) {
// ...
}
// この関数はいつもオブジェクト { ok, ... }を返すので、
// `if`文内にあるコードはいつも信用できます
if (checkIsAgeValid(age)) {
// ...
}✏️ リファクタリングしてみる
次のコードのように検証関数が一貫的に{ ok, ... }タイプのオブジェクトを返すようにすることができます。
typescript
/** ユーザーの名前は20字未満でないといけません */
function checkIsNameValid(name: string) {
if (name.length === 0) {
return {
ok: false,
reason: "名前は空の値ではいけません。"
};
}
if (name.length > 20) {
return {
ok: false,
reason: "名前は20字まで入力できます。"
};
}
return { ok: true };
}
/** ユーザーは年齢が18歳以上、99歳次の自然数でなければなりません */
function checkIsAgeValid(age: number) {
if (!Number.isInteger(age)) {
return {
ok: false,
reason: "年齢は整数でないといけません。"
};
}
if (age < 18) {
return {
ok: false,
reason: "年齢は18歳以上でないといけません。"
};
}
if (age > 99) {
return {
ok: false,
reason: "年齢は99歳以下でないといけません。"
};
}
return { ok: true };
}