typescript - Typescript:如何声明代表 Treeview 的类型?可能吗?
问题描述
Typescript 是 JavaScript 的 TYPED 超集,可以编译成 JavaScript,很好!它可以帮助我们减少一些错别字等,好的!我想创建一个接口,该接口将用作方法中的参数。此接口必须表示将用于解析对象的树视图。
示例:w1[a2].x[b02].y.z
是访问 in 值的z
路径myObject
const path = "w1[a2].x[b02].y.z";
const treeOfIdentifiers = {
"w1": {
key: "idLvlW",
"x": {
key: "idLvlX"
}
}
}
const myObject = {
w0: "Hello Root",
w1: [{
idLvlW: "a1",
x: [{
idLvlX: "b01",
y: {
z: "hello world from w1[a1].x[b01].y.z"
}
},
{
idLvlX: "b02",
y: {
z: "hello world from w1[a1].x[b02].y.z"
}
},
{
idLvlX: "b03",
y: {
z: "hello world from w1[a1].x[b03].y.z"
}
},
{
idLvlX: "b04",
y: {
z: "hello world from w1[a1].x[b04].y.z"
}
}
]
},
{
idLvlW: "a2",
x: [{
idLvlX: "b01",
y: {
z: "hello world from w1[a2].x[b01].y.z"
}
},
{
idLvlX: "b02",
y: {
z: "hello world from w1[a2].x[b02].y.z"
}
},
{
idLvlX: "b03",
y: {
z: "hello world from w1[a2].x[b03].y.z"
}
},
{
idLvlX: "b04",
y: {
z: "hello world from w1[a2].x[b04].y.z"
}
}
]
},
{
idLvlW: "a3",
x: [{
idLvlX: "b01",
y: {
z: "hello world from w1[a3].x[b01].y.z"
}
},
{
idLvlX: "b02",
y: {
z: "hello world from w1[a3].x[b02].y.z"
}
},
{
idLvlX: "b03",
y: {
z: "hello world from w1[a3].x[b03].y.z"
}
},
{
idLvlX: "b04",
y: {
z: "hello world from w1[a3].x[b04].y.z"
}
}
]
}
]
treeOfIdentifiers
如果我使用 TypeScript 进行编码, (如果不是any
!)的类型|接口是什么?问题是要确保 treeOfIdentifiers 的每个节点都提供属性key
,并且我们不知道treeOfIdentifiers
要解析的对象的结构,因为我们不知道要解析的对象的结构!
解决方案
这在 TypeScript 中是一件相当困难的事情。例如,将名为字符串值的属性添加key
到非string
值字典中并不直接用于强类型...并且可能意味着您可能希望使类型更简单(例如,每个节点都有一个key
属性和一个dictionary
属性) .
我什至没有考虑treeOfIdentifiers
在使用它 traverse 时确保它是有效的myObject
,因为你没有问过这个问题,这已经很复杂了。
但是,让我们看看使用映射类型和条件类型能走多远:
type NonRootOfTreeOfIdentifiers<T> = { [K in 'key' | keyof T]:
K extends 'key' ? string :
K extends keyof T ? NonRootOfTreeOfIdentifiers<T[K]> : never
};
type TreeOfIdentifiers<T> = { [K in keyof T]: NonRootOfTreeOfIdentifiers<T[K]> };
const asTreeOfIdentifiers = <T extends TreeOfIdentifiers<T>>(t: T): T => t;
其中最棘手的部分是在NonRootOfTreeOfIdentifiers<T>
. 它采用一个类型T
,表示有效的非根节点treeOfIdentifiers
。它将一个key
属性添加到T
,然后将这些属性映射T
到一个新类型:key
被映射为一个string
属性,而其他所有属性都映射到NonRootOfTreeOfIdentifiers<>
它自己的一个版本。因此NonRootOfTreeOfIdentifiers<T>
,遍历各个级别T
并将其转换为另一种类型。如果T
是一个有效的非根节点,那么NonRootOfTreeOfIdentifiers<T>
将是一个有效的非根节点。
type 函数表示根节点,TreeOfIdentifiers
不需要key
属性,否则向下遍历到NonRootOfTreeOfIdentifiers
.
最后,该asTreeOfIdentifiers
函数接受一个T
需要与 兼容的类型的参数TreeOfIdentifiers<T>
,并返回该参数。意思是,它只会接受符合规则的对象。所以它验证了它的论点,但没有改变它。
让我们看看它是否有效:
const treeOfIdentifiers = asTreeOfIdentifiers({
"w1": {
key: "idLvlW",
"x": {
key: "idLvlX"
}
}
});
编译,并被treeOfIdentifiers
推断为类型
{
"w1": {
key: string;
"x": {
key: string;
};
};
}
如果你以某种方式搞砸了,你会得到错误:
const missingKey = asTreeOfIdentifiers({
"w1": {
key: "idLvlW",
"x": {
kee: "idLvlX" // oops
}
}
});
// error: Property 'key' is missing in type '{ kee: string; }'
或者
const extraProperty = asTreeOfIdentifiers({
"w1": {
key: "idLvlW",
x: {
key: "idLvlX"
}
y: 123 // oops
}
});
// error: Types of property 'y' are incompatible.
// Type 'number' is not assignable to type 'NonRootOfTreeOfIdentifiers<number>'.
这样就可以了。不过,我不确定这对你是否值得。如果您尝试捕获文字字符串值类型"idLvlX"
(而不是仅string
),它会变得更加棘手。而且,正如我所说,我什至没有考虑过您将如何在类型系统中 myObject
进行验证。treeOfIdentifiers
无论如何,祝你好运。希望有帮助。
推荐阅读
- node.js - 如何编写中间件来修改节点js中的响应
- c# - Unity 如何通过 C# 脚本更改 Unity 的 3D 对象参数
- javascript - 如何更改多个数组对象的索引?
- kotlin - Kotlin flatten + 嵌套对象的映射
- java - Java ME 或 Java SE(Raspberry Pi 零)
- java - 为什么引用在内存中有大小,即使它没有引用此代码示例中的对象?
- linux - 在 apropos 命令中计算单词
- reactjs - componentDidUpdate 比 setState 中的实际值落后一步
- typescript - 将 JSON 作为类型导入
- javascript - 如何在 Inputmask 插件中设置范围?