Appearance
定义字典,增强代码可读性
沈孟平
最后更新于 2026-04-23 19:53:53
defineDict 适合把“数组配置”快速转成“按 key 访问的字典”,既减少手写循环,也能保留不错的类型推断。
能解决什么问题
- 从配置数组中提取某一列值
- 把枚举配置数组转成字典映射
- 把多列字段按主键聚合成对象
三种常见用法
1. 提取一列值
ts
const ids = defineDict(
[{ id: 1, name: 'A' }, { id: 2, name: 'B' }],
'id',
)
// [1, 2]2. 创建单值字典
ts
const nameMap = defineDict(
[{ id: 1, name: 'A' }, { id: 2, name: 'B' }],
'id',
'name',
)
// { 1: 'A', 2: 'B' }3. 创建多值字典
ts
const infoMap = defineDict(
[{ id: 1, name: 'A', age: 20 }, { id: 2, name: 'B', age: 30 }],
'id',
['name', 'age'],
)
// { 1: { name: 'A', age: 20 }, 2: { name: 'B', age: 30 } }源码签名
ts
const toRawType = (val: unknown) => Object.prototype.toString.call(val).slice(8, -1)
const isPropertyKey = (val: unknown): val is PropertyKey => ['String', 'Number', 'Symbol'].includes(toRawType(val))
function pick<T extends object>(target: T, keys: (keyof T)[]) {
return keys.reduce((dict, key) => ({ ...dict, [key]: target[key] }), {})
}
type ValidKeys<T, K extends keyof T = keyof T> = K extends K ? (T[K] extends PropertyKey ? K : never) : never
/**
* 将对象数组转换为字典或提取特定键的值数组
*
* @description
* 该函数有三种重载:
* 1. 提取对象数组中指定键的值数组
* 2. 创建以指定键为键,单个值为值的字典
* 3. 创建以指定键为键,多个值组成对象为值的字典
*
* @example
* // 用法1: 提取值数组
* defineDict([{id: 1, name: 'a'}, {id: 2, name: 'b'}], 'id')
* // 返回 [1, 2]
*
* // 用法2: 创建单值字典
* defineDict([{id: 1, name: 'a'}, {id: 2, name: 'b'}], 'id', 'name')
* // 返回 { 1: 'a', 2: 'b' }
*
* // 用法3: 创建多值字典
* defineDict([{id: 1, name: 'a', age: 20}, {id: 2, name: 'b', age: 30}], 'id', ['name', 'age'])
* // 返回 { 1: {name: 'a', age: 20}, 2: {name: 'b', age: 30} }
*
* @param defs - 源对象数组
* @param key - 用作键的属性名
* @param values - 可选,用作值的属性名(单个字符串)或属性名数组
* @returns 根据参数返回值数组或字典对象
*/
function defineDict<T extends object, K extends keyof T>(defs: T[], key: K): T[K][]
function defineDict<T extends object, K extends ValidKeys<T>, V extends keyof T>(
defs: T[],
key: K,
values: V
): Record<T[K] extends PropertyKey ? T[K] : never, T[V]>
function defineDict<T extends object, K extends ValidKeys<T>, V extends keyof T>(
defs: T[],
key: K,
values: V[]
): Record<T[K] extends PropertyKey ? T[K] : never, Pick<T, V>>
function defineDict<T extends object, K extends keyof T, V extends keyof T>(defs: T[], key: K, values?: V | V[]) {
if (typeof values === 'undefined') {
return defs.map(def => def[key])
}
return defs.reduce((map, def) => {
const _key = def[key]
if (!isPropertyKey(_key))
return map
const _val = Array.isArray(values) ? pick(def, values) : def[values]
return { ...map, [_key]: _val }
}, {})
}
export { defineDict }使用建议
key最好选择稳定且可作为对象键的字段,例如id、code、value。- 如果你只需要一列数组,直接传两个参数即可,不必再手写
map。 - 如果你需要高频查表,这种“数组转字典”的写法会比每次
find更直接。