首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何正确定义嵌套对象的泛型类型

如何正确定义嵌套对象的泛型类型
EN

Stack Overflow用户
提问于 2019-04-12 01:26:18
回答 1查看 1.1K关注 0票数 1

我正在尝试创建一个简单的递归函数,它将处理一个具有动态结构的对象,但我在类型方面遇到了问题。

代码语言:javascript
复制
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或其他任何东西)和一个可选字段,其中包含具有相同结构的对象数组(不一定是子对象)。

问题在

代码语言:javascript
复制
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]

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-04-12 02:14:14

recursive的原始定义中,泛型类型参数T是完全不受约束的,可以是任何类型。最重要的是,在类型级别上,当我们希望这样的泛型类型(string)的值有意义时,childPropidProp并不能真正对类型做出贡献。也就是说,我们希望为它们提供更多的文本类型。

尝试以下方法,尝试对我们正在寻找的对象的形状进行更通用的定义:

代码语言:javascript
复制
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命名的属性的对象,该对象是共享相同CKeyIKey的可选子对象数组。

{ [K in IKey]: number }:将具有由IKey命名的属性的对象描述为number

Record<string, unknown>:描述具有未知类型的附加属性的对象。我们使用unknown,这样使用它们会比使用any产生更好的错误,它会让你悄悄地脱离类型系统。这是用来说明对象上的附加属性是好的。

然后,我们将两者与&放在一起,说明对象必须满足所有约束。让我们看一个例子:

代码语言:javascript
复制
const t: MyElement<'children', 'testId'> = { testId: 30, children: [{ testId: 40 }] };

现在我们可以更新recursive的签名以利用新的约束:

代码语言:javascript
复制
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);
    });
  }
}

当然,还有一些测试来确保一切都如预期的那样进行类型检查:

代码语言:javascript
复制
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');

在操场上尝试一下(http://www.typescriptlang.org/play/#src=type%20MyElement%3CCKey%20extends%20string%2C%20IKey%20extends%20string%3E%0D%0A%20%20%3D%20%7B%20%5BK%20in%20CKey%5D%3F%3A%20MyElement%3CCKey%2C%20IKey%3E%5B%5D%20%7D%20%26%20%7B%20%5BK%20in%20IKey%5D%3A%20number%20%7D%20%26%20Record%3Cstring%2C%20unknown%3E%3B%0D%0A%0D%0Aconst%20t%3A%20MyElement%3C'children'%2C%20'testId'%3E%20%3D%20%7B%20testId%3A%2030%2C%20children%3A%20%5B%7B%20testId%3A%2040%20%7D%5D%20%7D%3B%0D%0A%0D%0Afunction%20recursive%3CCKey%20extends%20string%2C%20IKey%20extends%20string%3E(element%3A%20MyElement%3CCKey%2C%20IKey%3E%2C%20childProp%3A%20CKey%2C%20idProp%3A%20IKey%29%20%7B%0D%0A%20%20console.log(element%5BidProp%5D%2C%20childProp%2C%20element%5BchildProp%5D%29%3B%0D%0A%20%20if%20(element%5BchildProp%5D%29%20%7B%0D%0A%20%20%20%20element%5BchildProp%5D.forEach((el%29%20%3D%3E%20%7B%0D%0A%20%20%20%20%20%20recursive(el%2C%20childProp%2C%20idProp%29%3B%0D%0A%20%20%20%20%7D%29%3B%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Arecursive(%7B%20testId%3A%2010%20%7D%2C%20'children'%2C%20'testId'%29%3B%0D%0Arecursive(%7B%20testId%3A%2010%2C%20children%3A%20%5B%5D%2C%20anyprop%3A%20'something'%2C%20date%3A%20new%20Date(%29%20%7D%2C%20'children'%2C%20'testId'%29%3B%0D%0A%0D%0A%2F%2F%20Expected%20error%2C%20children%20elements%20must%20have%20a%20%60testId%60%0D%0Arecursive(%7B%20testId%3A%2010%2C%20children%3A%20%5B%7B%7D%5D%20%7D%2C%20'children'%2C%20'testId'%29%3B%0D%0Arecursive(%7B%20testId%3A%2010%2C%20children%3A%20%5B%7B%20testId%3A%2013%20%7D%5D%20%7D%2C%20'children'%2C%20'testId'%29%3B%0D%0A%0D%0Arecursive(%7B%20testId%3A%2010%2C%20children%3A%20%5B%7B%20testId%3A%2013%2C%20children%3A%20%5B%7B%20testId%3A%2015%20%7D%5D%20%7D%5D%20%7D%2C%20'children'%2C%20'testId'%29%3B%0D%0A%2F%2F%20Error%2C%20the%20deepest%20%60children%60%20must%20be%20an%20array%20our%20these%20id'd%20elements%0D%0Arecursive(%7B%20testId%3A%2010%2C%20children%3A%20%5B%7B%20testId%3A%2013%2C%20children%3A%20%7B%7D%20%7D%5D%20%7D%2C%20'children'%2C%20'testId'%29%3B)!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55638145

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档