イテレータを利用してスマートな実装しよう

該当のPR external_link

認証設定を保存する api を作成中。

改修前の実装ではオブジェクトプロパティに null が定義されていたとしても、そのまま保存されていました。
そのため、予期せぬ値が保存されてしまう可能性があります。

安全性を高めるために、値が null や undefined の場合、保存しないようにする必要がありました。

当初の実装

分割代入を使用しています。

before.js
const { twitterConsumerKey, twitterConsumerSecret, isSameUsernameTreatedAsIdenticalUser } = req.body; const requestParams = []; if (twitterConsumerKey != null) { requestParams.push({ 'security:passport-twitter:consumerKey': twitterConsumerKey }) } if (twitterConsumerSecret != null) { requestParams.push({ 'security:passport-twitter:consumerSecret': twitterConsumerSecret }) } if (isSameUsernameTreatedAsIdenticalUser != null) { requestParams.push({ 'security:passport-twitter:isSameUsernameTreatedAsIdenticalUser': isSameUsernameTreatedAsIdenticalUser }) }

かなり力技ですが、 value != null の場合のみ配列に入れるという実装をしていました。

もらったレビュー

予め

const bodyParamsKeyToConfigKeyMap = { twitterConsumerKey: 'security:passport-twitter:consumerKey', twitterConsumerSecret: 'security:passport-twitter:consumerSecret', isSameUsernameTreatedAsIdenticalUser: 'security:passport-twitter:isSameUsernameTreatedAsIdenticalUser', }

みたいなマッピングオブジェクトを作っておいて、Object.entries を使ってイテレートして、 もし req.body[key] が存在するなら requestParams.push({ (req.body[key]): req.body[value] }) みたいな処理をやればいいと思う。

行数はトータルで増えるけど、if ブロックや push 操作はイテレーション内に閉じ込められるため、ロジックが書かれた行が減る(マッピングの数にロジックの数が比例しなくなる)のでエンバグする可能性も減る。

すっかり抜け落ちていましたが、イテレータを使用して処理を抽象化することが可能でした。
今回は項目が 3 つですが、要素が増えれば増えるほど当初の実装では行数が増えて大変です。

完成形

抽象化に際して、改めて実現したいのは何なのか考え直しました。
実現したいのは、「 null じゃない時に 追加する 」のではなく、「 null だったら保存しない(除外する) 」でした。

なので、Object.entries を使用してオブジェクトから null を除外するコードを書きました。
非常に短くかけて上手く抽象化できました。

removeNullPropertyFromObject.js
// remove property if value is null const removeNullPropertyFromObject = (object) => { for (const [key, value] of Object.entries(object)) { if (value == null) { delete object[key] } } return object; }; module.exports = removeNullPropertyFromObject;

ライブラリフォルダである lib/ に作成することで他の場所にも使いまわせるようにしたため、使用部分は 数行の変更で済みました external_link

今回は twitter 認証部分ですが、今後別の認証部分やその他の設定画面でも使用できそうです。

まとめ

イテレータを利用することでスマートな実装ができました。

こういう util 書いて共通化したときはテストもあるとよい(その util だけでも)

とコメントもいただいているので次の課題はテストを書くことになりそうです✍️