键入自定义操作类型

时间:2019-11-10 21:42:45

标签: typescript redux

我正在按照本演讲中的设计模式进行操作(您无需观看即可了解我的问题): https://www.youtube.com/watch?v=5gl3cCB_26M

主要概念是为redux的类型赋予标识符。例如,api请求的操作创建者应具有以下类型:[Books] API_REQUEST。动作创建者基本上是将[Books]API_REQUEST组合在一起,然后将其作为类型添加到自己的动作中。 [Books]被赋予了动作创建者的思想观点。它允许同时对多个功能进行多个api请求,而不会在(例如)中间件中混淆它们。

所以这在纯Javascript中很简单。但是,使用Typescript,我们需要为中间件和reducer输入我们的操作。键入redux动作的主要思想是执行type: typeof API_REQUEST,以便Typescript可以根据其类型识别该动作(根据文档的这一部分:https://redux.js.org/recipes/usage-with-typescript)。

现在的问题是:如何键入Typescript无法识别的redux类型的动作(在本例中为API_REQUEST)?


这是一个具体的示例,因此您将更好地理解:

// types/api.ts

export const API_REQUEST = 'API_REQUEST';
export const API_SUCCESS = 'API_SUCCESS';

interface ApiRequestAction {
  type: string; // can't be `typeof API_REQUEST` because the final action will be `[Books] API_REQUEST`
  payload: {
    body: object | null;
  };
  meta: {
    method: 'GET' | 'POST' | 'PUT' | 'DELETE';
    url: string;
    feature: string;
  };
}

interface ApiSuccessAction {
  type: string; // same as before
  payload: {
    data: object[];
  };
  meta: {
    feature: string;
  };
}

export type ApiActions = ApiRequestAction | ApiSuccessAction;
// actions/api.ts

import { API_REQUEST, API_SUCCESS, ApiActions } from '../types/api';

export const apiRequest = ({ feature, body, method, url }): ApiActions => ({
  type: `${feature} ${API_REQUEST}`, // [Books] API_REQUEST
  payload: {
    body
  },
  meta: {,
    method,
    url,
    feature
  }
});

export const apiSuccess = ({ feature, data }): ApiActions => ({
  type: `${feature} ${API_SUCCESS}`, // [Books] API_SUCCESS
  payload: {
    data
  },
  meta: {
    feature
  }
});
// reducer/books.ts

import { API_REQUEST, ApiActions } from '../types/api';

export const booksReducer = (state = [], action: ApiActions) => {

  if (action.type === `[Books] ${API_REQUEST}`) {
    // Here's the issue, Typescript can't be like "Alright, in this block action should be the same as decribed in ApiRequestAction because of the condition. So he'll have access to `action.payload.method` and all the others..."
    // But nop, he's just giving an error because he can't know if the action is the same a ApiRequestAction or ApiSuccessAction.
    // This is because I can't use `typeof` in ApiRequestAction because the type of the action can't be known before the action is created.
    // Then Typescript is throwing an error because he can't know what the action is. And so he can't know if `action.method.url` can be accessed because is only in one of the two possible actions.
    console.log(action.meta.url); // Property 'url' does not exist on type '{ feature: string; }'

    // Do some stuff with `action`
  }
}

有没有办法解决这个问题?我考虑过某种普通的string(例如type: /\[\w+\] API_REQUEST/)正则表达式类型的集成,但是我认为这是不可能的。

我希望这是可以理解的,很难解释。如有任何疑问,请随时问我。

2 个答案:

答案 0 :(得分:2)

您可以使用以下功能强制转换界面:

export function isBooksRequest(action:any): action is ApiRequestAction {
  return action.type === `[Books] ${API_REQUEST}`; 
}

if (isBooksRequest(action)) {
  // action is considered as ApiRequestAction
  console.log(action.meta.url);
}

关键字istypeof type guards:请查看文档here

答案 1 :(得分:0)

根据@Wandrille的回答,我执行了以下功能,该功能使我能够与他的回答进行相同的比较,但采用的是更自适应的方式:

function checkAction<T>(action: any, comparison: string): action is T {
    return action.type === comparison;
}

// And use it like so in my reducer / middleware
if (checkAction<ApiRequestAction>(action, `${feature} ${API_REQUEST}`)) {
  console.log(action.meta.url)
}
相关问题