首页 > 解决方案 > 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

解决方案


这在 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

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


推荐阅读