[英]Typescript: How to declare a Type which represents a Treeview? Is it possible?
Typescript is a TYPED superset of JavaScript that compiles into JavaScript, fine! Typescript是JavaScript的TYPED超集,可以编译为JavaScript,很好! it helps us to reduce some typos etc etc ok!
它可以帮助我们减少一些错别字等,确定! I want to create an interface that would be used as an argument in a method.
我想创建一个在方法中用作参数的接口。 This interface has to represent a treeview that would be used to parse an object.
该接口必须表示将用于解析对象的树视图。
example: w1[a2].x[b02].yz
is the path to access value of z
in myObject
示例:
w1[a2].x[b02].yz
是访问myObject
中z
值的路径
const path = "w1[a2].x[b02].yz"; 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].yz" } }, { idLvlX: "b02", y: { z: "hello world from w1[a1].x[b02].yz" } }, { idLvlX: "b03", y: { z: "hello world from w1[a1].x[b03].yz" } }, { idLvlX: "b04", y: { z: "hello world from w1[a1].x[b04].yz" } } ] }, { idLvlW: "a2", x: [{ idLvlX: "b01", y: { z: "hello world from w1[a2].x[b01].yz" } }, { idLvlX: "b02", y: { z: "hello world from w1[a2].x[b02].yz" } }, { idLvlX: "b03", y: { z: "hello world from w1[a2].x[b03].yz" } }, { idLvlX: "b04", y: { z: "hello world from w1[a2].x[b04].yz" } } ] }, { idLvlW: "a3", x: [{ idLvlX: "b01", y: { z: "hello world from w1[a3].x[b01].yz" } }, { idLvlX: "b02", y: { z: "hello world from w1[a3].x[b02].yz" } }, { idLvlX: "b03", y: { z: "hello world from w1[a3].x[b03].yz" } }, { idLvlX: "b04", y: { z: "hello world from w1[a3].x[b04].yz" } } ] } ]
What would be the type|interface of treeOfIdentifiers
(if not any
!) if I code using TypeScript? 接口|什么是类型
treeOfIdentifiers
(如果没有any
如果使用打字稿我的代码!)? The thing is to ensure that each node of treeOfIdentifiers, the property key
would be provided and we don't know the structure for the treeOfIdentifiers
as we don't know the structure of the object to parse! 这样做的目的是确保将提供 treeOfIdentifiers的每个节点, 属性
key
,并且我们不知道treeOfIdentifiers
的结构,因为我们不知道要解析的对象的结构!
This is a fairly difficult thing to express in TypeScript. 这在TypeScript中很难表达。 For example, adding a string-valued property named
key
to a dictionary of non- string
values is not straightforward to strongly type... and possibly implies that you might want to make the type simpler (eg, each node has a key
property and a dictionary
property). 例如,将名称为
key
的字符串值属性添加到非string
值的字典中并不是很容易强类型化的操作……这可能意味着您可能希望使类型更简单(例如,每个节点都具有key
属性和dictionary
属性)。
I'm not even thinking of making sure that treeOfIdentifiers
is valid when using it to traverse myObject
, since you didn't ask about that and this is already very complicated. 我什至没有想到要在使用
treeOfIdentifiers
遍历myObject
时确保其有效,因为您没有对此提出treeOfIdentifiers
,而这已经非常复杂了。
But let's see how far we can get, using mapped and conditional types: 但是,让我们看看使用映射和条件类型可以达到的程度:
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;
The trickiest part of that is in NonRootOfTreeOfIdentifiers<T>
. 最棘手的部分在
NonRootOfTreeOfIdentifiers<T>
。 It takes a type T
representing a non-root node in a valid treeOfIdentifiers
. 它采用类型
T
表示有效的treeOfIdentifiers
的非根节点。 It adds a key
property to T
, and then maps these properties of T
into a new type: key
is mapped as a string
property, and every other property is mapped to a NonRootOfTreeOfIdentifiers<>
version of itself. 它将一个
key
属性添加到T
,然后将T
这些属性映射到一个新类型: key
被映射为一个string
属性,而所有其他属性都映射到其自身的NonRootOfTreeOfIdentifiers<>
版本。 So NonRootOfTreeOfIdentifiers<T>
walks down through the levels of T
and converts it to another type. 所以
NonRootOfTreeOfIdentifiers<T>
通过水平走下T
并将其转换为另一种类型。 If T
is a valid non-root node, then NonRootOfTreeOfIdentifiers<T>
will be a valid non-root node. 如果
T
是有效的非根节点,则NonRootOfTreeOfIdentifiers<T>
将是有效的非根节点。
The TreeOfIdentifiers
type function represents the root node, which doesn't require a key
property, but otherwise traverses down into NonRootOfTreeOfIdentifiers
. TreeOfIdentifiers
类型的函数表示根节点,该节点不需要key
属性,但否则会遍历到NonRootOfTreeOfIdentifiers
。
Finally, the asTreeOfIdentifiers
function takes an argument of type T
which is required to be compatible with TreeOfIdentifiers<T>
, and returns the argument. 最后,
asTreeOfIdentifiers
函数接受类型为T
的参数,该参数必须与TreeOfIdentifiers<T>
兼容,然后返回该参数。 Meaning, it will only accept objects conforming to the rules. 这意味着,它将仅接受符合规则的对象。 So it validates its argument but doesn't change it.
因此,它验证了其论点,但没有改变。
Let's see if it works: 让我们看看它是否有效:
const treeOfIdentifiers = asTreeOfIdentifiers({
"w1": {
key: "idLvlW",
"x": {
key: "idLvlX"
}
}
});
That compiles, and treeOfIdentifiers
is inferred to be of type 编译后,
treeOfIdentifiers
推断为类型
{
"w1": {
key: string;
"x": {
key: string;
};
};
}
If you mess up in some way, you will get errors: 如果您以某种方式搞砸了,则会收到错误消息:
const missingKey = asTreeOfIdentifiers({
"w1": {
key: "idLvlW",
"x": {
kee: "idLvlX" // oops
}
}
});
// error: Property 'key' is missing in type '{ kee: string; }'
or 要么
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>'.
So that all works as far as it goes. 这样一切都可以正常进行。 I'm not sure it's worth it to you, though.
不过,我不确定这是否值得。 It gets even hairier if you try to capture the literal string value types like
"idLvlX"
(instead of just string
). 如果您尝试捕获诸如
"idLvlX"
类的文字字符串值类型(而不只是string
),它会变得更加毛茸茸。 And, as I said, I haven't even thought about how you'd validate myObject
against treeOfIdentifiers
in the type system. 而且,正如我所说,我什至没有考虑过如何针对类型系统中的
treeOfIdentifiers
验证myObject
。
Anyway, good luck. 无论如何,祝你好运。 Hope that helped.
希望能有所帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.