自从学了 Typescript 之后,写 React 项目什么的就一直没离开过 TS 了。虽然 Typescript 大法好,不过鉴于 Typescript 严格的类型机制(事实上从某种意义上说一点都不严),导致在使用 Typescript 开发 React 的时候遇到了一些小小的问题(对 TS 好感度–)……这里就简单的记录一下。
使用 ES7 装饰符 (decorator) 时提示返回类型未定义
这个问题发生在使用 redux-zero (大概是个懒人版的 Redux)的时候。
一般来说,connect 一个组件大概要这样写,不管是用 react-redux 还是 redux-zero:
import { connect } from 'redux-zero/react';
/* ... */
class MyComponent extends React.Compoent {
render() {
return (/* ... */);
}
}
export default connect(mapStateToProps, actions)(MyComponent);
有时候我们可以偷懒用上 ES7 的 decorator 特性写得简洁一些:
@connect(mapStateToProps, actions)
export default class MyCompoent extends React.Component {
// ...
}
这两种写法效果是一样的。然而在 Typescript 中,以上代码会报这样的错误:
[ts] Unable to resolve signature of class decorator when called as an expression.
点一下 connect
方法的定义,可以看到在 connect.d.ts
的 17 行有这样的一句:
export default function connect(mapToProps: any, actions?: {}): (Child: any) => (props: any) => JSX.Element;
看着似乎没什么问题,但是:
// test 1
const test = (component: any) => {
console.log(component);
}
@test
class MyComponent extends React.Component {
// ...
}
// passed
把定义 test
的方法换一下:
// test 2
function test(component: any) {
console.log(component);
}
@test
class MyComponent extends React.Component {
// ...
}
// compile failed
看起来 Typescript 并不允许把用 function 定义的函数作为装饰符来使用呢。但是我们总不能去改依赖的代码吧,这个时候怎么办呢w
如果一些项目有 DefinitelyTyped 的 typing 文件的话,可以尝试安装 @types/xxxxx
, 例如 @types/react-redux
。
如果没有的话,答案就是自己再包上一层,以 redux-zero
的 connect()
为例:
// utils/connect.ts
import { connect as connectComponent } from 'redux-zero/react';
export const connect = (mapStateToProps: any, actions: any) => {
return (target: any) => (
connectComponent(mapStateToProps, actions)(target) as any
);
};
// component
import { connect } from 'utils/connect';
/* ... */
@connect(mapStateToProps, actions)
export defalt class MyComponent extends React.Component {
// ...
}
这样就可以啦。
自定义 JSX 元素在 JSX.IntrinsicElements 不存在
我并不知道怎么描述这个问题比较合适,场景是需要批量渲染一个对象中所有的组件:
const components = {
comp1: Compnent1,
comp2: Component2,
// ...
},
keys = Object.keys(components);
const res: Array<any> = [];
keys.forEach(key => {
const tmp: any = components[key];
res.push(<tmp key={key} />);
});
上面的代码会返回这样的错误:
[ts] Property 'tmp' does not exist on type 'JSX.IntrinsicElements'.
原因是 Typescript 要求 JSX 组件变量名的第一个字母为大写,如果不是的话 TS 便认为它不是个合法的 JSX 元素。所以把 tmp
改成 Tmp
就可以惹。