键入lodash函数的正确方法是什么?

时间:2018-12-06 08:43:51

标签: typescript redux react-redux lodash typescript-typings

我很难用lodash方法为通过选择器连接到redux存储的React组件编写正确的类型。

this article's conventions之后,组件的简化版本如下所示:

reducers / index.ts

interface ApiState {
  api: {
    entities: {
      subscriptions: {
        [pk: number]: {
          imageUrl: string;
          pk: number;
          slug: string;
          title: string;
        };
      };
    };
    result: {
      subscriptions: never[];
    };
  };
  …
};

export type RootState =
  | (ApiState &
      FirebaseState & {
        router: Reducer<RouterState, LocationChangeAction>;
      })
  | {};

src / components / MyComponent.tsx

import React, {PureComponent} from "react";
import {connect} from "react-redux";
import {RootState} from "~/reducers";

interface OwnProps {
  label: "host" | "experience";
  score: number;
  subscriptionPk: number;
}
interface StateProps {
  subscription?: {
    pk: number;
    title: string;
  };
}

const selectSubscriptions = state => state.api.entities.subscriptions;


const mapStateToProps = (state: RootState, ownProps: OwnProps): StateProps => ({
  subscription: _.find(selectSubscriptions(state), {
    pk: ownProps.subscriptionPk,
  }) as any,
});

type Props = StateProps & OwnProps;

export default connect<StateProps, {}, OwnProps>(mapStateToProps)(
  class extends PureComponent<Props> {
    render() {
      const {label, score, subscription} = this.props;

      return (
        <div>
          {label} {score} {subscription.title}
        </div>
      );
    }
  },
);

src / typings.d.ts

// lodash global typing - begin
declare namespace _ {} // eslint-disable-line no-unused-vars
// lodash global typing - end

上面的代码在lodash find方法强制转换为mapStateToProp中的subscription: _.find() as any,之前起作用。

如果我删除as any,则会出现以下错误:

$ tsc --project tsconfig.json
src/components/MyComponent.tsx:37:3 - error TS2322: Type '{ pk: { toString: ...; toFixed: ...; toExponential: ...; toPrecision: ...; valueOf: ...; toLocaleString: ...; [Symbol.iterator]: ...; }; } | undefined' is not assignable to type '{ pk: number; slug: string; title: string; } | undefined'.
  Type '{ pk: { toString: ...; toFixed: ...; toExponential: ...; toPrecision: ...; valueOf: ...; toLocaleString: ...; [Symbol.iterator]: ...; }; }' is missing the following properties from type '{ pk: number; slug: string; title: string; }': slug, title

37   subscription: _.find(selectSubscriptions(state), {
     ~~~~~~~~~~~~

  src/components/MyComponent.tsx:26:3
    26   subscription?: {
         ~~~~~~~~~~~~
    The expected type comes from property 'subscription' which is declared here on type 'StateProps'


Found 1 error.

为什么lodash不能正确找出类型?我宁愿不必将所有lodash调用都强制转换为任何类型...

使用的版本:

  • 反应16.6.3
  • redux 4.0.1
  • react-redux 5.1.1
  • lodash-webpack-plugin 0.11.5(本身使用lodash ^ 4.17.4)
  • @ types / lodash 4.14.119

1 个答案:

答案 0 :(得分:1)

请不要提供泛型类型来进行不必要的连接。

在这里

import _ from "lodash";
import { PureComponent } from "react";
import React from "react";
import { connect } from "react-redux";

interface IApiState {
  api: {
    entities: {
      subscriptions: {
        [pk: number]: {
          imageUrl: string;
          pk: number;
          slug: string;
          title: string;
        };
      };
    };
    result: {
      subscriptions: never[];
    };
  };
}

export type RootState = IApiState;

interface IOwnProps {
  label: "host" | "experience";
  score: number;
  subscriptionPk: number;
}
interface ISubscription {
  pk: number;
  title: string;
}
interface IStateProps {
  subscription?: ISubscription;
}

const selectSubscriptions = (state: RootState) =>
  state.api.entities.subscriptions;

const mapStateToProps = (
  state: RootState,
  ownProps: IOwnProps,
) => ({
  subscription: _.find(selectSubscriptions(state), {
    pk: ownProps.subscriptionPk,
  }),
});

type Props = IStateProps & IOwnProps;

export default connect(mapStateToProps)(
  class extends PureComponent<Props> {
    public render() {
      const { label, score, subscription } = this.props;

      return (
        <div>
          {label} {score} {subscription && subscription.title}
        </div>
      );
    }
  },
);