在React+DVA项目中愉快的使用TypeScript

3,070 阅读2分钟

我们在项目中使用DVA + TS的时候有如下痛点

  • dispatchtype无法保证书写的绝对正确
  • model里的effectput同样无法保证type的书写正确
// model里的代码
const model = {
    namespace: 'index',
    reducers: {
        dateState(state, {payload}) {
            return {...sate, ...payload}
        }
    }
}


// 组件内部代码
dispatch({
    type: 'indax/dataState',
    payload: {
        // ...
    }
})
  • effect里通过return返回的是一个promise,但是在组件内使用 .then去调用的时候,总是提示没有then方法

image.png

⚠️⚠️⚠️⚠️: 以下代码基于 Taro + Dva + Ts 的项目,有些类型无法直接从Dva中导入。例如:Model,Effect。如果可以,直接从Dva导入即可。

解决痛点1和3: 重写Dispatch类型

  • 我们可以通过重写Dispatch的类型,来使编辑器的提示更加智能
// 文件目录: src/type/index.ts

import {Action, Reducer, Dispatch} from "redux";

/**
 * ActionType, 推导当前effect & reducer
 * @default string
 */
type ActionType<M extends Model | string> = M extends Model
  ? `${M['namespace']}/${(keyof M['effects'] | keyof M['reducers']) & string}`
  : string;

type DispatchType = <S>(action: {
  type: ActionType<S extends Model ? S : string>,
  payload?: any
  [key: string]: any
}) => Promise<any>

export type DvaDispatch = DispatchType & Dispatch
// interface DvaDispatch extends DvaDispatchType, Dispatch {}

  • 使用方法
    • indexModel是model的类型定义,等下会说。
// 代码目录: src/index/index
import {connect} from 'react-redux';
import type {indexModel} from "./models";
import type {DvaDispatch} from "@/type";

const Index: React.FunctionComponent<{
    dispatch: DvaDispatch
}> = ({ dispatch }) => {
    
    const testDispatch = () => dispatch<indexModel>({
        type: 'index/updateState'
    })

    return (
        <View>
            <Text onClick={testDispatch}>点我无事发生</Text>
        </View>
    )
}

const mapStateToProps = (state: allModel) => {
    return { //... }
}
export default connect(mapStateToProps)(Index)

解决痛点2: 重写Model类型

  • 仍然是通过重写Model类型的方式来使编辑器提示更加智能
    • 因为在Taro里无法从Dva里导入Modal,所以直接把Modal的类型抄过来了
// 文件目录: src/type/index.ts

export type Effect = (action: AnyAction, effects: EffectsCommandMap) => void;
interface AnyAction extends Action {
  [extraProps: string]: any
}
interface EffectsCommandMap {
  put: <S>(action: {
    type: EffectAction<S extends Model ? S : string>,
    payload?: any
    [key: string]: any
  }) => any,
  call: Function,
  select: Function,
  take: Function,
  cancel: Function,
  [key: string]: any,
}

type EffectAction<M extends Model | string> = M extends Model
  ? `${M['namespace']}/${(keyof M['effects'] | keyof M['reducers']) & string}` | `${(keyof M['effects'] | keyof M['reducers']) & string}`
  : string;
  
export interface Model {
  namespace: string,
  state?: any,
  reducers?: ReducersMapObject | ReducersMapObjectWithEnhancer,
  effects?: EffectsMapObject,
  subscriptions?: SubscriptionsMapObject,
}

type ReducersMapObject<S = any, A extends Action = Action> = {
  [K in keyof S]: Reducer<S[K], A>
}
type ReducersMapObjectWithEnhancer = [ReducersMapObject, ReducerEnhancer];
interface ReducerEnhancer {
  (reducer: Reducer<any>): void,
}
interface EffectsMapObject {
  [key: string]: Effect | EffectWithType,
}
type EffectWithType = [Effect, { type: EffectType }];
type EffectType = 'takeEvery' | 'takeLatest' | 'watcher' | 'throttle';
interface SubscriptionsMapObject {
  [key: string]: Subscription,
}
type Subscription = (api: SubscriptionAPI, done: Function) => void;
interface SubscriptionAPI {
  history: History,
  dispatch: DvaDispatch,
}

  • 使用方式
// 文件目录: src/index/model/index
import { Reducer } from "redux";
import type {Effect, Model} from "@/type";

export interface indexModel extends Model{
  namespace: 'index';
  state: modelState;
  reducers: {
    updateState: Reducer<modelState>
  }
  effects: {
    getStudentsByWxId: Effect
    checkSchoolSet: Effect
  }
}
interface modelState {}

const Index: indexModel = {
  namespace: 'index',
  state: {},
  reducers: {
    updateState(state, { payload }) {
      return { ...state, ...payload }
    },
  },
  effects: {
    *getStudentsByWxId({ payload }, { put }) {
      yield put<indexModel>({
        type: 'updateState',
        payload: {//。。。},
      })
      return `我返回了一个promise`
    }
  }  
}

export default Index