Может ли mapStateToProps сузить типы реквизита? - PullRequest
4 голосов
/ 04 октября 2019

Я пытаюсь добавить аннотации типов к старому компоненту, и я не уверен, что типы connect поддерживают то, что я пытаюсь сделать. Компонент является связанным компонентом, который принимает реквизит с широким типом, затем использует этот реквизит для поиска значения в состоянии и выплевывает более узкий реквизит.

На данный момент я могу определить реквизиты с широким типом, и код будет работать. Однако любой код, который я пишу внутри компонента, должен соответствовать этому широкому типу, хотя я знаю, что он был сужен. Таким образом, чтобы удовлетворить машинопись, я должен либо добавить утверждения типа, либо ненужные проверки типов.

Вот упрощенный пример кода, показывающий поведение:

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)

В приведенном выше коде машинописьсправедливо указывает на то, что 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 таким образом, чтобы входили разные реквизиты, а не уходили? вне? Другими словами, мне нужен кто-то, чтобы иметь возможность визуализировать <Example thing={2} /> или <Example thing={"2"} />, и все же для кода внутри примера, чтобы знать, что благодаря mapStateToProps thing может быть только number

1 Ответ

3 голосов
/ 04 октября 2019

Подход 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.

Подход 2: Базовый интерфейс для параметров «out», «in» params расширяют базу

Альтернативой может быть объявление базового интерфейса, представляющего входящие объекты, и затем интерфейс, который расширяетон представляет пропау, уходящую - тогда вам нужно только указать типы, которые изменяются при выходе, так что вы не будете повторять много кода:

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
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...