我正在尝试创建一个简单的递归函数,它将处理一个具有动态结构的对象,但我在类型方面遇到了问题。
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
函数中,我得到了一个隐式的any error。嵌套的对象将有一些字段来指示它的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]
发布于 2019-04-12 02:14:14
在recursive
的原始定义中,泛型类型参数T
是完全不受约束的,可以是任何类型。最重要的是,在类型级别上,当我们希望这样的泛型类型(string
)的值有意义时,childProp
和idProp
并不能真正对类型做出贡献。也就是说,我们希望为它们提供更多的文本类型。
尝试以下方法,尝试对我们正在寻找的对象的形状进行更通用的定义:
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');
https://stackoverflow.com/questions/55638145
复制相似问题