Typescript:如何声明代表Treeview的Type?可能吗?

时间:2018-08-30 14:32:03

标签: typescript

Typescript是JavaScript的TYPED超集,可以编译为JavaScript,很好! 它可以帮助我们减少一些错别字等,确定! 我想创建一个在方法中用作参数的接口。该接口必须表示将用于解析对象的树视图。

示例:w1[a2].x[b02].y.z是访问zmyObject的值的路径

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的结构,因为我们不知道要解析的对象的结构!

1 个答案:

答案 0 :(得分:2)

这在TypeScript中很难表达。例如,将一个名为key的字符串值属性添加到非string值的字典中并不是很容易进行强类型的输入……并且可能暗示您可能希望使类型更简单(例如,每个节点都有一个key属性和一个dictionary属性)。

我什至没有想到要在使用treeOfIdentifiers遍历myObject时确保其有效,因为您没有对此提出疑问,而这已经非常复杂了。

但是让我们看一下使用mappedconditional类型可以得到的结果:

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

无论如何,祝你好运。希望有帮助。