Typescript是JavaScript的TYPED超集,可以编译为JavaScript,很好! 它可以帮助我们减少一些错别字等,确定! 我想创建一个在方法中用作参数的接口。该接口必须表示将用于解析对象的树视图。
示例:w1[a2].x[b02].y.z
是访问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"
}
}
]
}
]
如果我使用TypeScript进行编码,treeOfIdentifiers
(如果不是any
的类型|接口是什么?是为了确保treeOfIdentifiers的每个节点,将提供属性key
,我们不知道treeOfIdentifiers
的结构,因为我们不知道要解析的对象的结构!
答案 0 :(得分:2)
这在TypeScript中很难表达。例如,将一个名为key
的字符串值属性添加到非string
值的字典中并不是很容易进行强类型的输入……并且可能暗示您可能希望使类型更简单(例如,每个节点都有一个key
属性和一个dictionary
属性)。
我什至没有想到要在使用treeOfIdentifiers
遍历myObject
时确保其有效,因为您没有对此提出疑问,而这已经非常复杂了。
但是让我们看一下使用mapped和conditional类型可以得到的结果:
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>
将是有效的非根节点。
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
。
无论如何,祝你好运。希望有帮助。