我在使用extends keyof
时遇到了TS中类型系统的问题。在本例中,我有一个TreeContent
对象,但我希望树允许动态键,以指示构造函数使用的树对象中保存数据的位置。
我就像这样给全班打电话
const tree = new Tree<
TestTree,
"children",
"data",
TestTreeData,
"label"
>(treeTestData, "children", "data", "label");
children
键用于访问TestTree
中的子数据,data
和label
也是如此,但是label
将用于查找TestTreeData
中的label
。我在这里的目标是创建一个灵活的类,它可以从树数据中构造一个n叉树,可以为子节点和它存储的数据使用不同的别名。
export class Tree<
TreeContent,
ChildKey extends keyof TreeContent,
DataKey extends keyof TreeContent,
Data,
LabelKey extends keyof Data
>
{
id: string;
children: NodeModel<Data>[];
childrenKey: ChildKey;
dataKey: DataKey;
labelKey: LabelKey;
data: Data;
constructor(
treeContent: TreeContent,
_childrenKey: ChildKey,
_dataKey: DataKey,
_labelKey: LabelKey,
_id = uuidv4()
) {
this.dataKey = _dataKey;
this.childrenKey = _childrenKey;
this.labelKey = _labelKey;
this.children = [];
this.id = _id;
if (
!(_childrenKey in treeContent) ||
!Array.isArray(treeContent[this.childrenKey])
) {
throw Error(`Could not find data in ${String(this.childrenKey)}`);
}
const children = treeContent[this.childrenKey];
}
我省略了构造函数中的一些其他代码,因为我觉得它与问题无关。
问题是,children is const children: TreeContent[ChildKey]
不是数组的类型,即使我显式检查它是否是数组。我知道我可以铸造它,但我正在尽我所能,以避免铸造在我的情况。如果这是我剩下的选择,那很好,但我不知道我做错了什么。
我怀疑我可能完全走错路了。
我想要的是,我可以通知TS,我希望const children: TreeContent[ChildKey]
的值变成const children: TreeContent[]
,然后我可以用DataKey
复制逻辑,但TreeContent[DataKey]应该等于Generic数据对象。
编辑:这里是一个TSPlayground与错误显示,我不能使用推功能。
1条答案
按热度按时间wlzqhblo1#
您已经知道
ChildKey
和DataKey
generic类型参数被约束为TreeContent
类型参数的键。对于
DataKey
键上的值类型没有任何进一步的约束,因此没有明显的理由使用Data
类型参数all all;相反,您可以使用indexed access typeTreeContent[DataKey]
来引用您调用的Data
类型。另一方面,您需要约束
ChildKey
键处的值类型,使其成为TreeContent
数组。可以通过使用Record<K, V>
实用程序类型将TreeContent
约束为Record<ChildKey, TreeContent[]>
来实现这一点。(这并不意味着TreeContent
不能有比ChildKey
更多的属性;对象类型在TS中是开放的,并且被允许具有比所提到的更多的属性。这只是意味着ChildKey
必须是其中一个键,并且它的属性必须可分配给TreeContent[]
。一旦你做了这些改变,事情就应该开始按你想要的那样运行了:
注意,在调用
Tree
构造函数时,不需要手动指定类型参数。编译器会自动为您正确推断它们。Playground链接到代码