feat(redux-api-middleware): add types for dispatched actions (#38205)

This commit is contained in:
Andrew Luca 2019-09-11 08:39:47 +03:00 committed by Mine Starks
parent b54b6980a0
commit f9d157df87
2 changed files with 151 additions and 19 deletions

View File

@ -3,7 +3,7 @@
// Definitions by: Andrew Luca <https://github.com/iamandrewluca>
// Craig S <https://github.com/Mrman>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
// TypeScript Version: 3.0
import {
Middleware,
@ -46,7 +46,7 @@ export class RequestError extends Error {
constructor(message: string);
}
export class ApiError<T extends object = any> extends Error {
export class ApiError<T = any> extends Error {
name: 'ApiError';
status: number;
statusText: string;
@ -75,22 +75,38 @@ export interface RSAARequestTypeDescriptor<State = any, Payload = any, Meta = an
export interface RSAASuccessTypeDescriptor<State = any, Payload = any, Meta = any> {
type: string | symbol;
payload?: ((action: RSAAAction, state: State, res: Response) => Payload) | Payload;
meta?: ((action: RSAAAction, state: State, res: any) => Meta) | Meta;
meta?: ((action: RSAAAction, state: State, res: Response) => Meta) | Meta;
}
export interface RSAAFailureTypeDescriptor<State = any, Payload = any, Meta = any> {
type: string | symbol;
payload?: ((action: RSAAAction, state: State, res: Response) => Payload) | Payload;
meta?: ((action: RSAAAction, state: State, res: any) => Meta) | Meta;
meta?: ((action: RSAAAction, state: State, res: Response) => Meta) | Meta;
}
export type RSAARequestType<State = any, Payload = any, Meta = any> =
| string
| symbol
| RSAARequestTypeDescriptor<State, Payload, Meta>;
export type RSAASuccessType<State = any, Payload = any, Meta = any> =
| string
| symbol
| RSAASuccessTypeDescriptor<State, Payload, Meta>;
export type RSAAFailureType<State = any, Payload = any, Meta = any> =
| string
| symbol
| RSAAFailureTypeDescriptor<State, Payload, Meta>;
export interface RSAACall<State = any, Payload = any, Meta = any> {
endpoint: TypeOrResolver<State, string>;
method: string;
// `redux-api-middleware` strictly allows only this methods
method: 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
types: [
string | symbol | RSAARequestTypeDescriptor<State, Payload, Meta>,
string | symbol | RSAASuccessTypeDescriptor<State, Payload, Meta>,
string | symbol | RSAAFailureTypeDescriptor<State, Payload, Meta>
RSAARequestType<State, Payload, Meta>,
RSAASuccessType<State, Payload, Meta>,
RSAAFailureType<State, Payload, Meta>
];
body?: TypeOrResolver<State, BodyInit | null>;
headers?: TypeOrResolver<State, HeadersInit>;
@ -105,6 +121,33 @@ export interface RSAAAction<State = any, Payload = any, Meta = any> {
[RSAA]: RSAACall<State, Payload, Meta>;
}
/**
* `Promise<RSAARequestAction>` is not returned from dispatch like other actions
* Is only dispatched through redux
*/
export interface RSAARequestAction<Payload = any, Meta = any> {
type: string | symbol;
payload?: Payload | InvalidRSAA;
meta?: Meta;
error?: true;
}
export interface RSAASuccessAction<Payload = any, Meta = any> {
type: string | symbol;
payload: Payload | InternalError;
meta?: Meta;
error?: true;
}
export interface RSAAFailureAction<Payload = any, Meta = any> {
type: string | symbol;
payload: InternalError | RequestError | ApiError<Payload>;
meta?: Meta;
error: true;
}
export type RSAAActions = RSAARequestAction | RSAASuccessAction | RSAAFailureAction;
/**
* Redux behaviour changed by middleware, so overloads here
*/
@ -112,9 +155,12 @@ declare module 'redux' {
/*
* Overload to add api middleware support to Redux's dispatch() function.
* Useful for react-redux or any other library which could use this type.
* `Promise<undefined> is returned in case of RSAA validation errors or user bails out
*/
interface Dispatch {
(action: RSAAAction): Promise<any>;
<Payload = any, Meta = any>(
action: RSAAAction
): Promise<RSAASuccessAction<Payload, Meta> | RSAAFailureAction<Payload, Meta> | undefined>;
}
}

View File

@ -12,7 +12,13 @@ import {
createMiddleware,
apiMiddleware,
RSAAAction,
RSAACall
RSAACall,
RSAARequestTypeDescriptor,
RSAASuccessTypeDescriptor,
RSAAFailureTypeDescriptor,
RSAARequestAction,
RSAASuccessAction,
RSAAFailureAction,
} from 'redux-api-middleware';
{
@ -75,7 +81,9 @@ import {
new ApiError(200, 'OK', {}).response; // $ExpectType {}
new ApiError(); // $ExpectError
interface Response { data: number; }
interface Response {
data: number;
}
new ApiError<Response>(200, 'OK', { data: 0 }); // $ExpectType ApiError<Response>
new ApiError<Response>(200, 'OK', { data: 0 }).name; // $ExpectType "ApiError"
new ApiError<Response>(200, 'OK', { data: 0 }).status; // $ExpectType number
@ -117,11 +125,21 @@ import {
}
class StateDrivenRSAACall implements RSAACall<State> {
endpoint(state: State) { return state.path; }
headers(state: State) { return state.headers; }
options(state: State) { return state.options; }
body(state: State) { return state.body; }
bailout(state: State) { return state.bailout; }
endpoint(state: State) {
return state.path;
}
headers(state: State) {
return state.headers;
}
options(state: State) {
return state.options;
}
body(state: State) {
return state.body;
}
bailout(state: State) {
return state.bailout;
}
method: 'GET';
types: ['REQ_TYPE', 'SUCCESS_TYPE', 'FAILURE_TYPE'];
}
@ -138,11 +156,79 @@ import {
{
const store: Store = createStore(() => undefined);
store.dispatch({
const action: RSAAAction = {
[RSAA]: {
endpoint: '/test/endpoint',
method: 'GET',
types: ['REQ_TYPE', 'SUCCESS_TYPE', 'FAILURE_TYPE'],
}
});
},
};
store.dispatch(action);
}
{
const requestDescriptor0: RSAARequestTypeDescriptor<number, number, number> = {
type: '',
payload: 0,
meta: 0,
};
const requestDescriptor1: RSAARequestTypeDescriptor<number, number, number> = {
type: Symbol(),
payload: (action: RSAAAction, state: number) => state,
meta: (action: RSAAAction, state: number) => state,
};
const requestDescriptor2: RSAARequestTypeDescriptor<number, number, number> = {
type: 0, // $ExpectError
payload: '', // $ExpectError
meta: (action: RSAAAction, state: number) => '', // $ExpectError
};
const successDescriptor0: RSAASuccessTypeDescriptor<number, number, number> = {
type: '',
payload: 0,
meta: 0,
};
const successDescriptor1: RSAASuccessTypeDescriptor<number, number, number> = {
type: Symbol(),
payload: (action: RSAAAction, state: number, res: Response) => state,
meta: (action: RSAAAction, state: number, res: Response) => state,
};
const successDescriptor2: RSAASuccessTypeDescriptor<number, number, number> = {
type: 0, // $ExpectError
payload: '', // $ExpectError
meta: (action: RSAAAction, state: number) => '', // $ExpectError
};
const failureDescriptor0: RSAAFailureTypeDescriptor<number, number, number> = {
type: '',
payload: 0,
meta: 0,
};
const failureDescriptor1: RSAAFailureTypeDescriptor<number, number, number> = {
type: Symbol(),
payload: (action: RSAAAction, state: number, res: Response) => state,
meta: (action: RSAAAction, state: number, res: Response) => state,
};
const failureDescriptor2: RSAAFailureTypeDescriptor<number, number, number> = {
type: 0, // $ExpectError
payload: '', // $ExpectError
meta: (action: RSAAAction, state: number) => '', // $ExpectError
};
}
{
const requestAction0: RSAARequestAction<number, number> = {
type: ''
};
const successAction0: RSAASuccessAction<number, number> = {
type: '',
payload: 6
};
const failureAction0: RSAAFailureAction<number, number> = {
type: '',
payload: new ApiError(500, '', 1),
error: true
};
}