Я придерживаюсь шаблона проектирования из этого доклада (вам не нужно смотреть его, чтобы понять мою проблему): https://www.youtube.com/watch?v=5gl3cCB_26M
Основная концепция заключается в присвоении идентификатора типу избыточного числа. Например, создатель действия для запроса API должен иметь такой тип: [Books] API_REQUEST
. Создатель действия в основном комбинирует [Books]
и API_REQUEST
, а затем бросает его как тип в свое собственное действие. [Books]
приведены аргументы мысли создателя действия. Это позволяет выполнять несколько запросов API одновременно для нескольких функций и не путать их в (например) промежуточном программном обеспечении.
Так что это просто в чистом Javascript. Но с Typescript нам нужно напечатать наши действия для наших промежуточных программ и редукторов. Основная идея ввода действий приставки состоит в том, чтобы выполнить type: typeof API_REQUEST
, чтобы Typescript мог распознать действие по его типу (в соответствии с этой частью документа: https://redux.js.org/recipes/usage-with-typescript).
Теперь возникает вопрос: как можноЯ набираю действие (в данном случае для API_REQUEST
) с типом избыточности, который не распознается Typescript?
Вот конкретный пример, чтобы вы лучше понимали:
// 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`
}
}
Так есть ли способ это исправить? Я думал о каком-то типе регулярных выражений типа intead простого string
(например: type: /\[\w+\] API_REQUEST/
), но я не думаю, что это возможно.
Надеюсь, это понятно, это довольно сложно объяснить. Если у вас есть какие-либо вопросы, не стесняйтесь их задавать.