🙃

iterable keys から object 作るとき reduce 書くのだるいときの

2022/09/27に公開

Overview

Object.fromEntries - developer.mozilla.org とか便利そうやんけ! ... って読んだら

「チョット、そうじゃないんだよなぁ。。。」

と思い、サクッと object 生成する utility を typescript で書いた。

/**
 * iterable keys から object 作るとき reduce 書くのだるいときのやつ
 *
 * @example
 *   objectByKeys([1, 2, 3])
 *   // => { '1': undefined, '2': undefined, '3': undefined }
 *
 *   objectByKeys([1, 2, 3], 'initial')
 *   // => { '1': 'initial', '2': 'initial', '3': 'initial' }
 *
 *   objectByKeys([1, 2, 3], key => Number(key))
 *   // => { '1': 1, '2': 2, '3': 3 }
 *
 * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries
 */
export const objectByKeys = <T extends string|number|symbol, U = undefined>(
  keys: T[],
  initial?: U | ((key: T) => U),
): Record<T, U> => Object.fromEntries([
    ...keys.map(key => [
      key,
      initial instanceof Function ? initial(key) : initial,
    ])
  ])

Usage

こんな。

objectByKeys([1, 2, 3])
//=> { '1': undefined, '2': undefined, '3': undefined }

objectByKeys([1, 2, 3], 'initial')
// => { '1': 'initial', '2': 'initial', '3': 'initial' }

objectByKeys([1, 2, 3], key => Number(key))
// => { '1': 1, '2': 2, '3': 3 }

Usecase

redux で json を normalizr - github.com でこねこね → state に entities として突っ込みたいケド、正直 typescript だし型定義で十分 + normalizr ちゃん archived だしなぁ ... とかそういうとき用。

多くのケースでは createEntityAdapter - redux-toolkit.js.org を使い込んだほうがいい。

Refs

Normalizing State Shape - redux.js.org
Using createEntityAdapter with normalizr - redux-toolkit.js.org
Normalizrを使用したReduxの実装パターン - qiita.com


Appendix - Type inferable Object.keys()

json の keys を string union で型定義保ちながら整形しつつバラす、みたいなことしたかった。

/**
 * type inferable Object.keys
 */
export const objectKeys = <T extends object>(target: T): (keyof T)[] => (
  Object.keys(target).map(key => key as keyof T) // as 以外思いつかない...
)

/**
 * objectByKeys との組み合わせで json の keys を
 * string union で型定義しつつバラす例
 */
import { issues as issuesData } from 'data.json' // なんらか固定データ入ってるやつ

export type Issue = {
  key: keyof typeof issuesData
  subject: string
}

export const issues: Record<Issue['key'], Issue> = (
  objectByKeys(objectKeys(issuesData), key => ({
    key,
    subject: issuesData[key].subject.trim(), // 整形しつつシステム取り込み的な
  }))
)

console.log(
  issues['型推論可能なキー'].subject
)

Discussion