typescript 访问对象Map中不同类型的值

b91juud3  于 2023-04-13  发布在  TypeScript
关注(0)|答案(3)|浏览(172)

我有一些数据看起来像这样-

{
      System: 'VIT0056',
      Value: {
        Start: 3.3,
        End: 3.9
      },
      'Initial Range' : {
        'Start': '1/12/2022',
        'End': '31/12/2022',
      },
      Manager: 'Karl Woods',
      Location: 'Tokyo',
      Price: '$1.50',
    },

我现在在这个物体上做贴图就像这样-

{Object.entries(data).map(([key, val]) => {
              if (key === 'Value') {
                return (
                  <S.ValueContainer key={key}>
                    <Label>{key}:</Label>
                    <Text size="small">** DO SOMETHING HERE TO SHOW VALUES **</Text>
                  </S.ValueContainer>
                )
              }

              if (key === 'Initial Range') {
                return (
                  <S.ValueContainer key={key}>
                    <Label>{key}:</Label>
                    <Text size="small">** DO SOMETHING HERE TO SHOW VALUES ** </Text>
                  </S.ValueContainer>
                )
              }

              return (
                <S.ValueContainer key={key}>
                  <Label>{key}:</Label>
                  <Text size="small">{val}</Text>
                </S.ValueContainer>
              )
            }
         )
      }

对于值和初始范围键,我希望能够像这样显示它们-
数值:3.3 - 3.9
初始范围:从2022年12月1日至2022年12月31日
然而,我在访问这些值时遇到了麻烦,因为它们是嵌套对象。
当我把它们放在if语句中并尝试执行val.Start时,它告诉我-
类型“string”上不存在属性“Start|范围
但是我知道我在这个块中要访问的值只能是Range类型的。有没有一种方法来定义它?

anhgbhbe

anhgbhbe1#

您可以使用类型Assert来告诉编译器该值实际上是Range

<Text size="small">{(val as Range).Start} - {(val as Range).End}</Text>

也可以实现ToString(value: string | Range)函数

function ToString(value: string | Range) {
  //if value is a string, just return the value
  if (typeof value === "string") return value;

  //here the compiler knows it must be a Range
  //because it's not a string ...
  return `${value.Start} - ${value.End}`;
}

并在渲染中使用它

<Text size="small">{ToString(val)}</Text>

此外,您似乎也有不同类型的范围,因此在使用ToString()方法时,可能还需要对开始和结束属性进行类型检查

function ToString(value: string | Range) {
  if (typeof value === "string") return value;

  if (typeof value.Start === "number")
    return `${value.Start} - ${value.End}`;

 //here you know value is a range and
 //the properties of range are strings
 //(given both always have the same type

  //or apply appropriate formatting for your dates
  return `${value.Start} to ${value.End}`;

}

当然,你也可以返回一些渲染结果而不仅仅是字符串。

function ToRender(value: string | Range) {
  //if value is a string, just return the value
  if (typeof value === "string") return (<div>{value}</div>);

  //here the compiler knows it must be a Range
  //because it's not a string ...
  return (<div><span>{value.Start}</span> to <span>{value.End}</span></div>)
}
fhg3lkii

fhg3lkii2#

当你把对象传递给Object.entries时,你实际上是把键与它们关联的值的类型解耦了。看起来你正在迭代的数组的类型是[string, string | Range][],大概Range是在别处定义的某种自定义类型。
如果您希望以保持键和值类型之间关联的方式访问对象的属性,则应通过对象访问它们,而不是使用Map回调中可访问的val参数。
因此,尝试data[key].Start而不是val.Start(当然,一旦您缩小了key的类型)。

wnrlj8wa

wnrlj8wa3#

这是union类型的一个常见问题,错误存在的原因是当您有valstring时,阻止您尝试访问Start
首先检查类型,如下所示:

{Object.entries(data).map(([key, val]) => {
        if (typeof val !== "string") {
          if (key === "Value") {
            return (
              <S.ValueContainer key={key}>
                <Label>{key}:</Label>
                <Text size="small">** DO SOMETHING HERE TO SHOW VALUES **</Text>
              </S.ValueContainer>
            );
          }

          if (key === "Initial Range") {
            return (
              <S.ValueContainer key={key}>
                <Label>{key}:</Label>
                <Text size="small">** DO SOMETHING HERE TO SHOW VALUES ** </Text>
              </S.ValueContainer>
            );
          }
        }

        return (
          <S.ValueContainer key={key}>
            <Label>{key}:</Label>
            <Text size="small">{val}</Text>
          </S.ValueContainer>
        );
      })}

然后,这将始终返回到只显示val的值,然后对于其他部分,您可以自由访问Range类型的StartEnd

相关问题