#typescript #redux
#typescript #redux
Вопрос:
Я хочу, чтобы типы моих действий redux были статически типизированы, Чтобы я мог извлечь выгоду из обнаружения опечаток, автозаполнения и т. Д. В коде, Который имеет дело с действиями, такими как редукторы и т. Д.
Чтобы это работало хорошо, мне действительно нужно статически вводить структуру каждого действия в моем приложении следующим образом
type Actions =
| { type: 'A_PENDING' }
| { type: 'A_FULFILLED', payload: { a: number } }
| { type: 'A_REJECTED', error: Error }
| { type: 'B_PENDING' }
| { type: 'B_FULFILLED', payload: { b: number } }
| { type: 'B_REJECTED', error: Error }
| { type: 'C_PENDING' }
| { type: 'C_FULFILLED', payload: { c: number } }
| { type: 'C_REJECTED', error: Error }
Однако при написании этих типов для всех моих действий происходит много повторений.
Я знаю, что могу написать шаблоны редактора, чтобы буквально генерировать этот код для меня, но мне было интересно, есть ли какой-либо «собственный» способ TS генерировать такие шаблоны определений типов.
Я бы представил синтаксис примерно так (псевдокод)
typegenerator AsyncAction = (BaseString, PayloadType) =>
| { type: BaseString '_PENDING' }
| { type: BaseString '_FULFILLED' }, payload: PayloadType }
| { type: BaseString '_REJECTED' }, error: Error }
type Actions =
| AsyncAction('A', { a: number })
| AsyncAction('B', { b: number })
| AsyncAction('C', { c: number })
Существует ли что-нибудь подобное, или мне лучше просто выполнять генерацию буквального кода?
Ответ №1:
Ну, в настоящее время невозможно объединить строки на уровне типа. (аналогичный запрос на функцию здесь)
Но следующее может сделать код менее повторяющимся (если не менее повторяющимся, по крайней мере, более систематически повторяющимся)
type AsyncActionType = "A" | "B";
type AsyncActionPayloads = {
"A": { a: number },
"B": { b: string }
}
type PendingActionTypes = {
"A": "A_PENDING",
"B": "B_PENDING"
}
type FulfilledActionTypes = {
"A": "A_FULFILLED",
"B": "B_FULFILLED"
}
type RejectedActionTypes = {
"A": "A_REJECTED",
"B": "B_REJECTED"
}
type AsyncAction = {
[T in AsyncActionType]: {
type: PendingActionTypes[T]
} | {
type: FulfilledActionTypes[T],
payload: AsyncActionPayloads[T]
} | {
type: RejectedActionTypes[T],
error: Error
}
}[AsyncActionType];
type Action =
{ type: "MY_OTHER_NON_ASYNC_ACTION" } |
{ type: "MY_ANOTHER_NON_ASYNC_ACTION", payload: { foo: number } } |
AsyncAction;
Демонстрационная площадка. Наведите курсор AsyncAction
мыши, и вы увидите, что это объединение всех асинхронных действий, которые вы ожидаете. Также попробуйте удалить "B": "B_PENDING"
из PendingActionTypes
, вы получите ошибку компиляции.
Также вы видите раздражающие части PendingActionTypes
FulfilledActionTypes
и RejectedActionTypes
можете быть перемещены в другой файл, который может быть сгенерирован с помощью скрипта nodejs, который считывает содержащий файл AsyncActionType
и вызывает его при изменении этого файла
Комментарии:
1. Спасибо и 1, этот способ сделать это довольно интересен. Это почти полностью соответствует тому, что я считал возможным, но да, тот факт, что вам все равно придется вручную вводить строки, вызывает сожаление. Хотя вы ответили на мой вопрос, это невозможно (пока). Полезно знать.
2. @davnicwil Добро пожаловать! 🙂 Может быть, однажды, когда это будет возможно, я обновлю этот ответ, заменив
PendingActionTypes[T]
наT "_PENDING"
xD
Ответ №2:
Мне тоже хочется опубликовать этот ответ, потому что он ближе (на самом деле довольно точный) к вашему псевдокоду. Я не публиковал это ранее, потому что это не уменьшает повторяемость, а просто вытесняет ее. (может быть, делает его более повторяющимся? idk). Если я игнорирую extends "A" | "B"
which требует согласованности и делает его повторяющимся, мне нравится это решение больше.
type AsyncAction<T extends "A" | "B", P> =
| { type: AddSuffix<T, "PENDING"> }
| { type: AddSuffix<T, "FULFILLED">, payload: P }
| { type: AddSuffix<T, "REJECTED">, error: Error }
type Action =
| AsyncAction<"A", { a: string }>
| AsyncAction<"B", { b: string }>
type AddSuffix<
T extends "A" | "B",
S extends "PENDING" | "FULFILLED" | "REJECTED"
> =
T extends "A" ?
S extends "PENDING" ? "A_PENDING" :
S extends "FULFILLED" ? "A_FULFILLED" :
S extends "REJECTED" ? "A_REJECTED" :
never :
T extends "B" ?
S extends "PENDING" ? "B_PENDING" :
S extends "FULFILLED" ? "B_FULFILLED" :
S extends "REJECTED" ? "B_REJECTED" :
never :
never;
<a rel=»noreferrer noopener nofollow» href=»https:///www.typescriptlang.org/play/#src=type AsyncAction =
| { type: AddSuffix }
| { type: AddSuffix, payload: P }
| { type: AddSuffix, error: Error }
type Action =
| AsyncAction
| AsyncAction
type AddSuffix =
T extends «A» ?
S extends «PENDING» ? «A_PENDING» :
S extends «FULFILLED» ? «A_FULFILLED» :
S extends «REJECTED» ? «A_REJECTED» :
never :
T extends «B» ?
S extends «PENDING» ? «B_PENDING» :
S extends «FULFILLED» ? «B_FULFILLED» :
S extends «REJECTED» ? «B_REJECTED» :
never :
never;» rel=»nofollow noreferrer»>Демонстрационная площадка