reactjs - mapStateToProps 可以缩小 props 的类型吗?

标签 reactjs typescript react-redux

我正在尝试向旧组件添加类型注释,但不确定是否 connect的类型支持我正在尝试做的事情。该组件是一个连接的组件,它接受一个广泛类型的 prop,然后使用该 prop 查找状态中的值,并吐出一个更窄的 prop。

目前,我可以用广泛的类型定义 Prop ,代码就可以工作了。但是,我在组件中编写的任何代码都必须符合这种广泛的类型,即使我知道它已经缩小了。因此,为了满足 typescript ,我要么必须添加类型断言,要么必须进行不必要的类型检查。

下面是一个简化的代码示例,用于显示该行为:

interface ExampleProps {
  thing: string | number;
}

const Example extends React.Component<ExampleProps> {
  render() {
    const problem = this.props.thing * 2; // type error

    // Workaround, but losing some typesafety
    // const notAProblem = (this.props.thing as number) * 2;

    // Workaround, but doing unnecessary checks
    // if (typeof this.props.thing === 'number') {
    //   const notAProblem = this.props.thing * 2;
    // }

    return null;
  }
}

const mapStateToProps = (state: RootState, otherProps: ExampleProps) => {
  if (typeof otherProps.thing === 'string') {
    return {
      thing: state.lookup[otherProps.thing]; //resolves to a number
    }
  }
  return {
    thing, // also a number
  }
}

export default connect(mapStateToProps)(Example)

在上面的代码中,typescript 正确地指出了 const problem: number = this.props.thing * 2;是个问题。使用我定义的类型,this.props.thing可能是一个字符串,所以它会正确给出错误 The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.

是否可以使用 connect以不同的 Prop 进来而不是出去的方式?换句话说,我需要有人能够渲染 <Example thing={2} /><Example thing={"2"} /> ,但要让示例中的代码知道这一点,多亏了 mapStateToProps,thing只能是 number

最佳答案

方法 1:每个字段的通用类型

一种方法是使“事物”成为 ExampleProps 上的通用类型,即:

interface ExampleProps<T extends (string | number)> {
  thing: T;
}

然后你可以分别在类和函数类型签名中指定不同的具体类型:

class Example extends Component<ExampleProps<number>> {
  render() {
    const problem: number = this.props.thing * 2;
    return null;
  }
}

const mapRootStateToExampleProps = (state: RootState, {thing}: ExampleProps<string | number>) => {
  if (typeof thing === 'string') {
    return {
      thing: state.lookup[thing] //resolves to a number
    }
  }
  return {
    thing, // also a number
  }
}

不过,这可能不是最佳解决方案,因为您的类型签名可能会很快变得冗长。它只对 ExampleProps 接口(interface)的一个或两个字段真正有意义。

方法 2:“out”参数的基础接口(interface),“in”参数扩展基础

另一种方法是声明一个表示传入 Prop 的基本接口(interface),然后声明一个扩展它的接口(interface)表示传出的 Prop - 然后您只需要指定在“传出”时修改的类型,这样您没有重复很多代码:

interface ExamplePropsIn {
  thing: string | number;
  other: string;
}

interface ExamplePropsOut extends ExamplePropsIn {
    thing; number;
}

class Example extends Component<ExamplePropsOut> {
  render() {
    const problem: number = this.props.thing * 2;
    return null;
  }
}

const mapRootStateToExampleProps = (state: RootState, {thing}: ExamplePropsIn) => {
  if (typeof thing === 'string') {
    return {
      thing: state.lookup[thing] //resolves to a number
    }
  }
  return {
    thing, // also a number
  }
}

关于reactjs - mapStateToProps 可以缩小 props 的类型吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58241408/

相关文章:

javascript - 如何在 react native 中单击按钮打开 react native 模式?

typescript - 使用 Firebase 函数发出 http 请求时找不到地址

javascript - typescript :在对象的多维数组中分组(按ID)

typescript - RxJS typescript : Property 'reduce' does not exist on type 'Observable<number>'

javascript - 单击“查看页面源代码”时,ReactJS 不显示 HTML

reactjs - 将属性传递给组件与通过 redux store 访问

javascript - 如何在 React.js 中动态添加和删除表格行

javascript - 在 React 中通过数组索引 onClick 递增

react-router - 在redux中间件中使用react-router进行重定向

javascript - React Native with react-navigation and Redux - 如何实现全局可用的 'Sign Out' 按钮