reactjs React useState允许对象属性不在接口中

roejwanj  于 2023-06-29  发布在  React
关注(0)|答案(2)|浏览(146)

我注意到在Typescript中使用useState时出现了意想不到的行为,它允许分配任何属性,即使该属性在接口中不存在。例如:

interface ComponentState {
    foo: string,
    bar: boolean
}

const [state, setState] = useState<ComponentState>({ foo: 'foo', bar: false });

const onAction = () => {
    setState((prevState) => {
        ...prevState,
        bar: true,
        help: 'why is this allowed?'
    });
}

为什么typescript不抱怨“help”属性?如果我尝试将bar(布尔值)设置为字符串,它会抱怨。
这和prevState对象的传播有关系吗?
我不喜欢这样,因为它允许错误通过。我错过了什么吗?我查看了useState的定义,没有发现它允许[key: string]: any

ujv3wf0j

ujv3wf0j1#

这不是React特有的,而是TypeScript结构化类型系统的一个工件。
简单地说,在许多情况下,TypeScript允许额外的属性,因为你从setState()函数返回的对象仍然满足ComponentState,即使它有额外的属性。
一方面,这通常很好,不会真正影响行为,因为代码中其他地方的逻辑将不允许访问额外的属性,它们是某种无法访问的幻影属性- TypeScript不会让你访问,因为它们不是ComponentState定义的一部分。我们通常更关心的是确保我们有我们期望的正确属性,并且需要在对象上,而不是有没有预期的额外属性。
另一方面,这不一定是可取的,因为您可能会意外地在某个地方添加额外的属性,以为它们在其他地方可见/可用,即使它们不是,或者绊倒其他读取属性的逻辑,并且不期望额外的属性。例如,如果你在某个地方使用Object.keys(),假设确切的属性是已知的,但结果是还有其他的属性,那会怎么样?
解决这个问题的一种方法是使用更显式的类型定义,这通常是不需要的,但在这里可能更有用。例如:

function MyComponent() {
  const [state, setState] = React.useState<ComponentState>({ foo: 'foo', bar: false });

  const onAction = () => {
    setState((prevState) => {
      const result: ComponentState = {
        ...prevState,
        bar: true,
        help: 'why is this allowed?'
      };

      return result;
    });
  }
}

当你试图定义result时,这会给予你一个错误。
这样会得到错误而不是直接返回类型的原因是过多的属性检查。TypeScript只在某些情况下这样做:
对象文字会得到特殊处理,并在将它们分配给其他变量或将它们作为参数传递时进行过多的属性检查。

uxh89sit

uxh89sit2#

我将保留jered的答案,但我想补充的是,我们可以通过简单地声明函数的返回类型来获得更清晰的解决方案:

setState((prevState): ComponentState => {
    ...prevState,
    bar: true,
    help: 'Typescript correctly performs excess property checking'
});

相关问题