TYPESCRIPT
Typescript针对树这一数据结构声明类型的思考

3 min read
#Typescript场景
后端接口数据:
[
{
label: 'xxx',
value: 1,
// 第一层一定包含children字段
children: [
{
label: 'xxx',
value: 1,
// 第二层也一定包含children字段
children: [
{
label: 'xxx',
value: 1,
// 第三层不一定包含children字段
children: [],
},
],
},
],
},
];
简单来说就是一个树的嵌套中 前两层与第三层的数据结构不固定但相似度极高 这时候是采用复用的方式 or 冗余一层特殊的结构来进行声明呢 让我陷入了沉思🤔
前端逻辑:
/*伪代码*/
interface ItemBase {
label: string
value: number
children?: ItemBase[] // 因为要兼容第三层的Item数据 所以这里children变成可选的
}
// select函数其实永远接收的是前面两层item的数据
function select({ children }: ItemBase) {
return children.join()
}
// 模拟后端请求
const data = get<ItemBase[]>() // <- data: ItemBase[]
// 因为这里children有可能是undefined 与select函数参数类型不符
// 即在定义的过程中为了复用相同结构 兼容了children为可选的情况
select(data[0].children) // <- ts type error ❌
改造方案
- 优雅的硬写 📓 (extending-types)
/*添加为什么要定义这么套娃娃中娃的注释*/
interface ItemBase {
label: string
value: number
}
interface Item extends ItemBase {
children: ItemSecond[]
}
interface ItemSecond extends ItemBase{
children: ItemThird[]
}
interface ItemThird extends ItemBase {
children?: Item[]
}
const data = get<Item[]>()
select(data[0].children) // ✅
- 优雅还带点可读性的复用 📒 (non-null-assertion-operator)
interface ItemBase {
label: string
value: number
children?: ItemBase[]
}
const data = get<ItemBase[]>()
// 通过断言的方式来特殊处理当前的业务逻辑情况
// 添加关于当前接口结构要这么处理的注释是极好的
select(data[0].children!) // ✅
优雅的跪下来求后端大佬不要传空或者不传children
字段回来 🧎♂️
说在最后
综上所述都是可以稍微优雅地解决这样特殊树结构的Typescript定义方式 相信你看完以后也有自己心里满意的答案了 适合自己的就是最好的嗷
End 🎉
(当然我最后选的是第二种解决方式 业务相关的我喜欢写注释在业务代码里 逃 🏃