近期在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优势又是麻烦。欢迎交流看法~