最近在看antd源碼,也在自學一些typescript的語法以及使用,而後準備帶在學校的師弟作一個音樂播放器做爲typescript的練習踩踩坑;css
而後居然沒想到在項目剛開始就深陷大坑--搭建react-redux項目流程,一下內容就是記錄我是怎樣從這個深坑中爬出來的過程。node
從項目建立以後就掉坑裏了。。。react
建立項目的流程很簡單,可有參考我以前寫的這篇文章git
剛開始還不是很熟悉typescript,因此我最開始的reducer, action, constant,文件都是以.js結尾,只有組件的代碼是以.tsx結尾的,而後就開始按照流程,將store中的state進行mapStateToProps以及actions進行mapDispatchToProps,誰想到這居然是噩夢的開始,最開始我是這樣寫的代碼:github
import { bindActionCreators } from 'redux';
function mapStateToProps(state: {}) {
return {
player: state.PlayerStore,
};
}
function mapDispatchToProps(dispatch: Function) {
return {
playerActions: bindActionCreators(actions, dispatch)
};
}複製代碼
而後就開始報錯了。。。typescript
幾經折騰以後我又將代碼改爲了這樣,才得以過關redux
import { bindActionCreators, Dispatch } from 'redux';
function mapStateToProps(state: { PlayerStore: object }) {
return {
player: state.PlayerStore,
};
}
function mapDispatchToProps(dispatch: Dispatch<{}>) {
return {
playerActions: bindActionCreators<{}>(actions, dispatch)
};
}複製代碼
interface PlayerPropsClass {
player: PlayerStateTypes;
playerActions: actions.PlayerActionsTypes;
}
class Player extends React.Component<PlayerPropsClass, {}> {
constructor(props: object) {
super(props as PlayerPropsClass);
this.state = {};
}
addOne = (num: number) => this.props.playerActions.count(num);
subtractOne = (num: number) => this.props.playerActions.subtract(num);
render() {
const { countNumber } = this.props.player.toJS();
return (
<div className="player"> <span>{countNumber}</span> <button onClick={() => this.addOne(countNumber as number)}>點擊+1</button> <button onClick={() => this.subtractOne(countNumber as number)}>點擊-1</button> </div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Player as any);複製代碼
這裏的(Player as any)報錯提示bash
[tslint] Type declaration of 'any' loses type-safety. Consider replacing it with a more precise type, the empty type ('{}'), or suppress this occurrence. (no-any)複製代碼
而後我改爲antd
export default connect(mapStateToProps, mapDispatchToProps)(Player as {});複製代碼
仍然報錯less
[ts]
類型「{}」的參數不能賦給類型「ComponentType<{ player: object; } & { playerActions: {}; }>」的參數。
不能將類型「{}」分配給類型「StatelessComponent<{ player: object; } & { playerActions: {}; }>」。
類型「{}」提供的內容與簽名「(props: { player: object; } & { playerActions: {}; } & { children?: ReactNode; }, context?: any): ReactElement<any> | null」不匹配。複製代碼
哇心態爆炸,都不知道怎麼寫這個斷言。。。而後我想起了antd的一個例子,寫的是React.ReactElement
,而後偶然間在網上找到了說的是在node_modules中有一個@type文件夾就是typescript對於node包中的相應的類型匹配,因而我就按照這個路徑node_modules/@types/react/index.d.ts
,終於找到了React.ComponentType<T>
,這個文件裏面還有不少的類型,想要了解的能夠本身去看看
export default connect(mapStateToProps, mapDispatchToProps)(Player as React.ComponentType<PlayerPropsClass>);複製代碼
有了以上的經驗,再加上在這裏看了一些代碼,我決定把actions和reducer也改成ts結尾的文件;
src/reducers/index.ts
import { combineReducers, createStore } from 'redux';
import PlayerStore from '../containers/Player/reducer';
// 這個是用來使用到mapStateToProps函數的地方給state進行類型檢測的
export interface AppStoreType {
PlayerStore: object;
}
const rootReducer = combineReducers({
PlayerStore,
});
export default () => {
return createStore(rootReducer);
};複製代碼
containers/Demo/reducer.ts
import * as PlayerTypes from './constant';
import { fromJS } from 'immutable';
// 因爲使用到immutable,state上面就會使用到immutable的函數,因此須要將其也作成類型檢測
import { ImmutableFuncType } from '../../constant';
interface ActionType {
type: string;
countNumber?: number;
}
interface StoreType {
countNumber: number;
}
// 把當前容器的state組,而後拋出提供使用
export type PlayerStateTypes = StoreType & ImmutableFuncType;
const PlayerStore: PlayerStateTypes = fromJS({
countNumber: 0,
});
export default (state = PlayerStore, action: ActionType) => {
switch (action.type) {
case PlayerTypes.COUNT:
return state.update('countNumber', () => fromJS(action.countNumber));
default:
return state;
}
};複製代碼
containers/Demo/action.ts
import * as PlayerTypes from './constant';
export const count = (num: number) => ({
type: PlayerTypes.COUNT,
countNumber: num + 1,
});
export const subtract = (num: number) => ({
type: PlayerTypes.COUNT,
countNumber: num - 1,
});
// 拋出actions函數的類型以供mapDispatchToProps使用
export interface PlayerActionsTypes {
count: Function;
subtract: Function;
}複製代碼
containers/Demo/index.tsx
import * as React from 'react';
import './style.css';
import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import * as actions from './action';
import { PlayerStateTypes } from './reducer';
import { AppStoreType } from '../../reducers';
interface PlayerPropsClass {
player: PlayerStateTypes;
playerActions: actions.PlayerActionsTypes;
}
function mapStateToProps(state: AppStoreType) {
return {
player: state.PlayerStore,
};
}
function mapDispatchToProps(dispatch: Dispatch<{}>) {
return {
playerActions: bindActionCreators<{}>(actions, dispatch)
};
}
class Player extends React.Component<PlayerPropsClass, {}> {
constructor(props: object) {
super(props as PlayerPropsClass);
this.state = {};
}
addOne = (num: number) => this.props.playerActions.count(num);
subtractOne = (num: number) => this.props.playerActions.subtract(num);
render() {
const { countNumber } = this.props.player.toJS();
return (
<div className="player"> <span>{countNumber}</span> <button onClick={() => this.addOne(countNumber as number)}>點擊+1</button> <button onClick={() => this.subtractOne(countNumber as number)}>點擊-1</button> </div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Player as React.ComponentType<PlayerPropsClass>);複製代碼
經過這個小練習還真的是感覺到了typescript的好處,那就是在編譯時就報錯,並且可以知道報錯緣由,不須要在編譯完成之後再去進行錯誤點的查找,確實節省了不少時間,尤爲是對於JavaScript代碼的undefined和null的處理很友好。。總之,感受寫起來很流暢啊。啊哈哈哈哈~~~