typescript - 如何正确定义嵌套对象的泛型类型
问题描述
我正在尝试创建一个简单的递归函数,该函数将处理具有动态结构的对象并且我遇到了类型问题。
interface Nested {
id: number;
children?: Nested[];
}
interface Props<T> {
elements: T[];
childProp: string;
idProp: string;
}
function recursive<T>(element: T, childProp: string, idProp: string) {
console.log(element[idProp], childProp, element[childProp]);
if (element[childProp]) {
element[childProp].forEach((el: T) => {
recursive<T>(el, childProp, idProp);
});
}
}
function test<T>(props: Props<T>) {
props.elements.forEach((element) => {
recursive<T>(element, props.childProp, props.idProp);
});
}
const nested: Nested[] = [
{
id: 1,
children: [
{
id: 2,
children: [
{
id: 3
}
]
},
{
id: 4,
children: [
]
},
]
},
{
id: 5
}
]
test<Nested>({
elements: nested,
childProp: 'children',
idProp: 'id'
});
从技术上讲,代码有效,但在recursive
函数中我得到一个隐含的任何错误。嵌套对象将有一些字段指示它的 id(不总是 id,可以是 categoryId 或其他任何东西)和一个包含具有相同结构的对象数组(不总是子对象)的可选字段。
问题在
function recursive<T>(element: T, childProp: string, idProp: string) {
console.log(element[idProp], childProp, element[childProp]);
if (element[childProp]) {
element[childProp].forEach((el: T) => {
recursive<T>(el, childProp, idProp);
});
}
}
element[idProp]
和_element[childProp]
解决方案
在您的原始定义中recursive
,泛型类型参数T
完全不受约束,可以是任何东西。最重要的是,在类型级别,当我们希望它们的值有意义时,childProp
并idProp
不能真正为具有这种泛型类型 ( ) 的类型做出贡献。string
即我们想要为他们提供更多的文字类型。
尝试以下尝试对我们正在寻找的对象的形状进行更通用的定义:
type MyElement<CKey extends string, IKey extends string>
= { [K in CKey]?: MyElement<CKey, IKey>[] } & { [K in IKey]: number } & Record<string, any>;
{ [K in CKey]?: MyElement<CKey, IKey>[] }
: 描述一个对象,其属性名为 byCKey
是一个可选的子数组,它们共享相同的CKey
和IKey
。
{ [K in IKey]: number }
: 描述一个对象,其属性命名IKey
为 a number
。
Record<string, unknown>
: 描述具有未知类型的附加属性的对象。我们使用unknown
这样的方式,使用它们会产生更好的错误,而不是any
默默地让你脱离类型系统。这用于表示对象的附加属性很好。
然后我们将两者放在一起&
说对象必须满足所有约束。看一个例子:
const t: MyElement<'children', 'testId'> = { testId: 30, children: [{ testId: 40 }] };
现在我们可以更新 的签名recursive
以利用新的约束:
function recursive<CKey extends string, IKey extends string>(element: MyElement<CKey, IKey>, childProp: CKey, idProp: IKey) {
console.log(element[idProp], childProp, element[childProp]);
if (element[childProp]) {
element[childProp].forEach(el => {
recursive(el, childProp, idProp);
});
}
}
当然还有一些测试来确保一切都按预期进行类型检查:
recursive({ testId: 10 }, 'children', 'testId');
recursive({ testId: 10, children: [], anyprop: 'something', date: new Date() }, 'children', 'testId');
// Expected error, children elements must have a `testId`
recursive({ testId: 10, children: [{}] }, 'children', 'testId');
recursive({ testId: 10, children: [{ testId: 13 }] }, 'children', 'testId');
recursive({ testId: 10, children: [{ testId: 13, children: [{ testId: 15 }] }] }, 'children', 'testId');
// Expected error, the deepest `children` must be an array our these id'd elements
recursive({ testId: 10, children: [{ testId: 13, children: {} }] }, 'children', 'testId');
在操场上试试吧!
推荐阅读
- java - 使用 for 循环从 Java 中的多个类对象运行类方法
- javascript - 将数组的字符串表示形式转换回数组
- java - 使用 Gson 将 Java 对象列表转换为 Json 中的正确名称值对格式
- json - 从命令行参数生成 JSON
- excel - 尝试从 Twitter 中的 GET 请求中获取 ResponseText
- spring-boot - Eureka 客户端的快速失败行为
- php - PHP 按值对多维数组进行排序
- reactjs - 无法访问 onPanResponderMove 内的更新状态或道具
- python-3.x - Pyqt5 和设计器中的视频小部件
- javascript - React-Native 项目选择