如何在打字稿中使用动态字段名称键入树

时间:2020-09-12 15:12:49

标签: typescript

示例:

interface Tree {
    [key: string]: Tree | {name: string}
}


const t: Tree = {
    b: { name: 'test 1' },
    c: {
        d: { name: 'test 2' }
    },
    e: {
        f: {
            g: { name: 'test 3' }
        }
    }
}

const { b, c, e } = t

const testName1 =  b.name // name
const testName2 =  c.d.name // Error Property 'd' does not exist on type 'Tree | { name: string; }'.  Property 'd' does not exist on type '{ name: string; }'.(2339
const testName3 =  e.f.g.name // Error Property 'f' does not exist on type 'Tree | { name: string; }'.  Property 'f' does not exist on type '{ name: string; }'.(2339)

无法访问财产c.de.f

类型Tree有什么问题?

可以使用这种方法:

const { d } =  c as Tree
const testName2 = d.name

但是也许有更方便的方法吗?

typescript playground

1 个答案:

答案 0 :(得分:1)

编译器是正确的:这是一种容易出错的处理方式。您将其用t注释为Tree的事实意味着您告诉编译器忘记它知道的任何特定类型信息,并假定它是一棵通用树。

这会引起问题:在通用树(如您所定义的树)中,任何时候都可能有子树:因此c.d可能只是树本身。如果您没有将其用于树遍历或类似的事情,并且需要确保存在特定的树结构,那么您需要其他的东西。另外,按照定义方式,您将为所有可能的字符串键定义一个子树,没有任何未定义的东西。

如果您想将其用于常规树类型输入,我将像这样更改您的定义并添加一个类型保护。 Playground link

interface TreeLeaf {
  name: string;
}

interface Tree {
  [key: string]: Tree | TreeLeaf | undefined
}

const t: Tree = {
  b: { name: 'test 1' },
  c: {
    d: { name: 'test 2' }
  },
  e: {
    f: {
      g: { name: 'test 3' }
    }
  }
};

function isLeaf(val: Tree | TreeLeaf | undefined): val is TreeLeaf {
  return !!val && typeof val.name === "string";
}

const { b, c, e } = t

if (isLeaf(b)) {
  console.log(b.name);
}

if (c && !isLeaf(c) && isLeaf(c.d)) {
  const testName2 =  c.d.name // No longer an error
}