首页 > 解决方案 > InteliJ:Typescript 根据接口自动检查 {} 对象?

问题描述

这是错误连接到 Redux 的 React 组件的示例违规代码。

import React from 'react';
import { View, StyleSheet, ViewStyle, Text } from 'react-native';
import { connect } from "react-redux";
import { StoreState } from "../../store/storeState/storeState";
import { LogFactory, Logger } from "../../vlabs-js/general/logging/log";

interface DevCompProps {
    counter: number
}

class UnconnectedDevComp extends React.Component<DevCompProps> {
    private logger: Logger;

    constructor(props: DevCompProps) {
        super(props);

        this.logger = LogFactory.fromComp('DevComp');
    }

    render() {
        this.logger.info(`render(): props='${this.props.counter}'`);

        return <View style={{flexDirection: 'row', borderWidth: 3, borderColor: 'black', padding: 5}}>
            <Text>Counter:</Text>
            <Text>{this.props.counter}</Text>
        </View>
    }
}

const mapStateToProps = (state: StoreState) => {
    return {
        number: state.devState.counter
    };
};


export const DevComp = connect(mapStateToProps)(UnconnectedDevComp)

为了使代码正常工作,mapStateToProps 应该是


const mapStateToProps = (state: StoreState) => {
    return {
        counter: state.devState.counter
    };
};

代替


const mapStateToProps = (state: StoreState) => {
    return {
        number: state.devState.counter
    };
};

有没有办法编译/InteliJ-Warn 检查 mapStateToProps 返回的对象是否与接口匹配

interface DevCompProps {
    counter: number
}

所以这样的错误会被 IDE 标记出来,而不必手动发现。

标签: reactjstypescriptintellij-idearedux

解决方案


简短的回答是“不”,因为这里的所有解决方案都很糟糕。处理函数组件和useSelector钩子可能会更好。

当您使用错误的键进行映射时,您的组合DevComp将是一个组件,counter当您调用它时需要传入一个道具,以便打字稿正确地获取该部分。

您可以通过在函数上设置一些泛型来引发错误connect,但这似乎很痛苦。我们要设置的变量是第一个,它是从 返回的 props mapStateToProps,第三个是你希望调用组件的 props。

const DevComp = connect<DevCompProps, {}, {}>(mapStateToProps)(UnconnectedDevComp)

mapStateToProps同样,您可以通过将返回类型设置为来引发错误Partial<DevCompProps>

您需要覆盖react-redux包中的某些类型才能自动获得该推断。但这也很糟糕。

我们想让它返回的 propsmapStateToProps不能包含组件 props 中不存在的任何键。

你不能通过操纵MapStateToProps类型来做到这一点。这有三个变量,但没有一个代表要映射的组件的类型。你需要的是TStateProps被限制。

export type MapStateToProps<TStateProps, TOwnProps, State = DefaultRootState> =
    (state: State, ownProps: TOwnProps) => TStateProps;

connect归根结底,问题在于HOC的两步性质。“连接器”的创建与将其应用于组件是分开的,因此连接器不能依赖于该组件。

这样做完全没问题:

const myWrapper = connect(mapStateToProps);

并获取类型

InferableComponentEnhancerWithProps<{ number: number; } & DispatchProp<AnyAction>, {}>

myWrapper是一个可以包装任何组件并为其添加道具的函数number。它不知道要应用的组件类型,也不关心该组件是否真的需要 prop number

您需要定义自己的函数,将这两个步骤组合成一个带有两个参数的函数,以便参数可以相互推断类型。我没有完整的解决方案。 connect有 15 个不同的重载,这个类型签名只描述了一个。此外,实现是一个错误,但函数类型可以满足您的需求。

const myConnect = <InnerProps, MappedProps extends Partial<InnerProps> , State = DefaultRootState>(
  mapStateToProps: (state: State) => MappedProps, Component: ComponentType<InnerProps>
  ): ComponentType<Omit<InnerProps, keyof MappedProps>> => {
    return connect(mapStateToProps)(Component);
}

export const DevComp = myConnect(mapStateToProps, UnconnectedDevComp)

推荐阅读