TypeScript+React+Loadable遇到的类型错误(ts:2345)

3,660 阅读2分钟

近期在react项目中使用了typescript来开发,然后使用react-Loadable做异步加载,然后报类型错误。ts错误提示如下:

代码如下:

---index.tsx

class CourseRoom extends React.PureComponent<IProps> {
    constructor(props) {
        super(props);
    }
    render() {
        return <div className={style.CourseRoom}>CourseRoom</div>
    }
}
export default ({ routeId, actions }) => {
    const mapDispatchToProps = dispatch => ({
        getCourseUrl: param => dispatch(actions.getCourseUrl(param))
    })
    const mapStateToProps = _state => {
        const state = _state.get(routeId);
        return {
            students: state.get('students'),
        }
    }
    return connect(mapStateToProps, mapDispatchToProps)(CourseRoom)
};

---loadbale.tsx

export default (config) => Loadable(
    {
        loading: LoadIndicator,
        loader: () => import(/* webpackChunkName: "CourseRoom" */ './index').then(exports => {
            return exports.default(config);
        }),
    }
);

尝试解决一

看报错提示Loadble要传递render来实现自定义渲染,看过react-Loadable源码后,render接收两个参数loaded和props,loaded就是loader方法执行后得到的Promise的resolve的结果也就是return exports.default(config) 的结果,然后把代码改制如下。
export default (config) => Loadable(
    {
        loading: LoadIndicator,
        loader: () => import(/* webpackChunkName: "CourseRoom" */ './index').then(exports => {
            return exports.default(config);
        }),
        // react-Loadable源码内的提示写法
        render: (loaded, props) => {
            return <loaded {...props} />;
        }
    });
但是依然会报错:'loaded' refers to a value, but is being used as a type here.ts(2749),ts把这种写法当做了类型。

尝试解决二

查看react-Loadable类型定义文件发现,loader需要返回的Promise的结果要包含这两种类型
loader(): Promise<React.ComponentType<Props> | { default: React.ComponentType<Props> }>;
然后我火速又查看了connect源码,发现返回结果类型并没有错啊,很头疼。。。,当我把connect直接去掉,index和loadable改成如下,竟然正常运行,直接懵逼,难道connect处理过的返回结果真的不一样么?
---index.tsx

export default class CourseRoom extends React.PureComponent<IProps> {
    constructor(props) {
        super(props);
    }
    render() {
        return <div className={style.CourseRoom}>CourseRoom</div>
    }
}

---loadbale.tsx

export default (config) => Loadable(
    {
        loading: LoadIndicator,
        loader: () => import(/* webpackChunkName: "CourseRoom" */ './index').then(exports => {
            return exports.default;
        }),
    }
);
然后我又把index.tsx的代码改回原来状态,loadable.tsx改成如下
方式一:
export default (config) => Loadable(
    {
        loading: LoadIndicator,
        loader: () => import(/* webpackChunkName: "CourseRoom" */ './index').then(exports => {
            return exports.default(config);
        }),
        render: (loaded, props) => {
            return React.createElement(Reflect.has(loaded, 'default') ? loaded['default'] : loaded, props);
        }
    });
方式二:
export default (config) => Loadable(
    {
        loading: LoadIndicator,
        loader: () => import(/* webpackChunkName: "CourseRoom" */ './index').then(exports => {
            const component = exports.default(config);
            return Reflect.has(component, 'default') ? component['default'] : component;
        }),
    });
得以解决。原来loader和loaded都包含多种类型(如下),需要判断是否是包含default属性的对象,不得不说ts着实严谨(哎,无奈。。)
loader(): Promise<React.ComponentType<Props> | { default: React.ComponentType<Props> }>;

(parameter) loaded: React.ComponentClass<never, any> | React.FunctionComponent<never> | {
    default: React.ComponentType<never>;
}

最终解决

虽然解决了问题,但是感觉并不理想。又专注在loadbale.tsx中寻找解决方式,经过多次尝试,有如下两种解决方式。代码如下:
方式一:去掉了render,在loader的后面多加了个.then()
export default (config) => Loadable(
    {
        loading: LoadIndicator,
        loader: () => import(/* webpackChunkName: "CourseRoom" */ './index').then(exports => {
            return exports.default(config);
        }).then(),
    }
);

方式二: 将loader先拎出来声明,然后再赋值,不过此方法只能解决编写时的类型检查,并不能解决运行时(pass)
export default (config) => {
    const loader = () => import(/* webpackChunkName: "home" */ './index').then(exports => {
        return exports.default(config);
    })
    Loadable({
        loading: LoadIndicator,
        loader,
    })
};
这个问题就是类型对应问题,既是ts优势又是麻烦。欢迎交流看法~