联合类型:不能在forloop中使用第二个可选类型

时间:2018-05-13 23:38:32

标签: angular typescript union-types

我有一个数组,我想循环,但我有麻烦,因为联合类型。我的问题是我从下面的代码中遗漏了什么,所以visual studio代码没有看到我对该特定数组有第二个可选类型?

class Menu {
  // name of the menu
 name: string;
  // list of the ingredients in this menu
  // or a list of submenu with its ingredients
 list: string[] | Menu[];
 hasSubList?: boolean;
}

...
menu: Menu[];

ngOnInit() {
  this.loadMenu();
}
ngAfterContentInit() {
  this.print();
}
loadMenu(): void {
  this.menu = [
   {
     name: "toast" 
     list: [
      "slide of bread",
     ],
   },
   {
     name: "Crumble eggs on taste",
     hasSubList: true;
     list: [
      {
        name: "Eggs",
        list: [
         {
           "Eggs",
           "pepper",
           "a pinch of salt",
          }
         ],
       },
       {
        name: "Toast",
        list: [
          "a slide of bread"
         ],
        },
       ],
   },
 ];
}

this print(): void {
  for(let i=0; i<this.menu.length;i++){
    let item = this.menu[i];
    console.log(item.name);
    for(let j=0; i<item.list.length; j++){
      let list = item.list[j];
      if(item.hasSubList) {
         // HERE
         // console intellsense says 
         // "property 'list' does not exist on type 'string | Menu'
         // "property 'list' does not exist on type 'string'
        for(let k=0; k< list.list.length; k++}(
          console.log(list.list[k]);
        }
      } else {
       console.log(list);
      }
}

回顾一下intellsense显示的信息;

"property 'list' does not exist on type 'string | Menu'
"property 'list' does not exist on type 'string'

为什么没有检查菜单?因为&#39; list&#39;存在类型&#39;菜单&#39;

3 个答案:

答案 0 :(得分:2)

由于list[j]的类型为string | Menu,因此使用它的代码必须能够处理 字符串或菜单,它不能只处理一个或另一个。我认为您依赖的知识是hasSubList为真,那么list始终是菜单列表。如果那是你的意思,那么你可以将item投射到一个菜单(list as Menu),不过一般情况下最好避免这样的演员,因为他们限制了多少类型检查器可以捕获代码中的真正错误。

答案 1 :(得分:2)

如@jacobm所述,编译器根据 hasSubList 无法知道联合类型的实际类型。

您必须使用一种方法来区分编译器可以理解的内容。 1这样做的方法是检查项目本身的类型,然后根本不需要 hasSubList 属性。

e.g。

for(let i=0; i<this.menu.length;i++){
    let item = this.menu[i];

    for(let j=0; i<item.list.length; j++){
        let list = item.list[j];

        // typeof operator can help discriminate the union type
        if(typeof item !== 'string') {
            for(let k=0; k< list.list.length; k++}(
                console.log(list.list[k]);
            }
        } else {
            console.log(list);
        }
    }
}

这是使用for..of循环而不是for循环的另一个示例。这可能是一种风格偏好,但我建议在不使用循环索引的情况下使用它们,因为它们更简洁,IMO更易读。

for (let item of menu) {
    for (let list of item.list) {
        if(item !== 'string') {
            for(let childItem of list.list) {
                console.log(childItem);
            }
        } else {
            console.log(list);
        }
    }
}

答案 2 :(得分:2)

消息是由Typescript引起的,因为您将其指定为def remove_punctuation(value): return re.sub('[!#?]', '', value) clean_ops = [str.strip, remove_punctuation, str.title] def clean_strings(strings, ops): result = [] for value in strings: for function in ops: value = function(value) result.append(value) return result print(clean_strings(states, clean_ops)) ,因此无法确定要使用的类型。使用强制转换是解决此问题的一种方法。

string | Menu[]