1. 背景
我们有时需要从嵌套结构获取对应的数据,比如以下结构:
const nested = {
a: {
b: {
c: 10,
},
},
};
通过a.b.c读取,但如果某个字段的值可能为空,会导致报错
我们可以实现一个getDeepObjByKeys方法去解决
getDeepObjByKeys(data,path,defaultValue)
getDeepObjByKeys(nested,"a.b.d","defaultValue")
// 读取不到对应的数据,返回 "defaultValue"
getDeepObjByKeys(nested,"a.b.c","defaultValue")
// 读取到对应的数据,返回 10
参数名 | 说明 | 默认值 |
data | 需要获取值的对象 | - |
path | 对象路径,键名通过'.'进行连接 | - |
defaultValue | 默认值 | undefined |
2. 实现
2.1 功能实现
功能的实现,主要通过循环去操作
将path拆分为数组,进行遍历,遍历过程访问当前key对应的value,一旦value为null或undefined,结束遍历,返回默认值。否则继续遍历。
function isUndefinedOrNull(value) {
return typeof value === "undefined" || value === null;
}
function getDeepObjByKeys(data, path, defaultValue = undefined) {
const keys = path.split(".");
let current = data;
for (const key of keys) {
if (isUndefinedOrNull(current) || isUndefinedOrNull(current[key])) {
return defaultValue;
}
current = current[key];
}
return current;
}
功能的实现其实不难,但我们希望通过ts去约束path的类型,让编辑器能否给出自动提示,实现类似效果:
2.2 TypeScript 类型实现
先实现一个ObjPath类型,接受一个泛型作为参数,约束泛型的类型为object。
泛型计算后,返回的类型是由object的key拼接的类型,是一个字符串的联合类型。
实现如下:
type ObjPath<Data extends object> = {
[Key in keyof Data & (string | number)]: Data[Key] extends object
? `${Key}` | `${Key}.${ObjPath<Data[Key]>}`
: `${Key}`;
}[keyof Data & (string | number)];
遍历键值对,判断值的类型是否为object,如果是,递归。
实现没有什么难度,但需要约束keyof Data的类型为number或者string。
以及[keyof Data & (string | number)]去约束递归的时候,每一层keyof都是number或者string。
调整我们的代码实现:
function isUndefinedOrNull(value: unknown) {
return typeof value === "undefined" || value === null;
}
function getDeepObjByKeys<T extends object, U = unknown>(
data: T,
path: ObjPath<T>,
defaultValue?: U
) {
const keys = path.split(".");
let current: Record<string, any> = data;
for (const key of keys) {
if (isUndefinedOrNull(current) || isUndefinedOrNull(current[key])) {
return defaultValue;
}
current = current[key];
}
return current;
}