我正在尝试创建一个简单的递归函数,该函数将处理具有动态结构的对象,并且打字时遇到问题。
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]
答案 0 :(得分:0)
在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>[] }
:将具有CKey
命名的属性的对象描述为共享相同的CKey
和IKey
的子级的可选数组。
{ [K in IKey]: number }
:将具有IKey
命名的对象描述为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');
在the playground中试用!