Skip to content

Commit a855bd3

Browse files
Update types to support mapError properly
1 parent 8ba85ea commit a855bd3

File tree

4 files changed

+325
-24
lines changed

4 files changed

+325
-24
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { createStore, Event } from 'effector';
2+
import { describe, test, expectTypeOf } from 'vitest';
3+
4+
import { unknownContract } from '../../contract/unknown_contract';
5+
import { declareParams } from '../../remote_operation/params';
6+
import { createJsonMutation } from '../create_json_mutation';
7+
import { JsonApiRequestError } from '../../fetch/api';
8+
import { ExecutionMeta } from '../../remote_operation/type';
9+
10+
describe('createJsonMutation', () => {
11+
describe('mapError', () => {
12+
test('callback receives correct types', () => {
13+
createJsonMutation({
14+
params: declareParams<string>(),
15+
request: { url: 'http://api.salo.com', method: 'POST' as const },
16+
response: {
17+
contract: unknownContract,
18+
mapError: ({ error, params, headers }) => {
19+
expectTypeOf(error).toEqualTypeOf<JsonApiRequestError>();
20+
expectTypeOf(params).toEqualTypeOf<string>();
21+
expectTypeOf(headers).toEqualTypeOf<Headers | undefined>();
22+
23+
return { code: 'ERROR', message: 'test' };
24+
},
25+
},
26+
});
27+
});
28+
29+
test('callback receives void params when no params declared', () => {
30+
createJsonMutation({
31+
request: { url: 'http://api.salo.com', method: 'POST' as const },
32+
response: {
33+
contract: unknownContract,
34+
mapError: ({ error, params, headers }) => {
35+
expectTypeOf(error).toEqualTypeOf<JsonApiRequestError>();
36+
expectTypeOf(params).toEqualTypeOf<void>();
37+
expectTypeOf(headers).toEqualTypeOf<Headers | undefined>();
38+
39+
return { code: 'ERROR' };
40+
},
41+
},
42+
});
43+
});
44+
45+
test('return type is used for finished.failure event', () => {
46+
const mutation = createJsonMutation({
47+
params: declareParams<string>(),
48+
request: { url: 'http://api.salo.com', method: 'POST' as const },
49+
response: {
50+
contract: unknownContract,
51+
mapError: () => ({ code: 'ERROR', message: 'test' } as const),
52+
},
53+
});
54+
55+
expectTypeOf(mutation.finished.failure).toEqualTypeOf<
56+
Event<{
57+
error: { readonly code: 'ERROR'; readonly message: 'test' };
58+
params: string;
59+
meta: ExecutionMeta;
60+
}>
61+
>();
62+
});
63+
64+
test('sourced callback receives source value', () => {
65+
createJsonMutation({
66+
params: declareParams<string>(),
67+
request: { url: 'http://api.salo.com', method: 'POST' as const },
68+
response: {
69+
contract: unknownContract,
70+
mapError: {
71+
source: createStore({ defaultMessage: 'Unknown error' }),
72+
fn: ({ error, params, headers }, source) => {
73+
expectTypeOf(error).toEqualTypeOf<JsonApiRequestError>();
74+
expectTypeOf(params).toEqualTypeOf<string>();
75+
expectTypeOf(headers).toEqualTypeOf<Headers | undefined>();
76+
expectTypeOf(source).toEqualTypeOf<{ defaultMessage: string }>();
77+
78+
return { code: 'ERROR', message: source.defaultMessage };
79+
},
80+
},
81+
},
82+
});
83+
});
84+
85+
test('sourced callback return type is used for finished.failure', () => {
86+
const mutation = createJsonMutation({
87+
params: declareParams<string>(),
88+
request: { url: 'http://api.salo.com', method: 'POST' as const },
89+
response: {
90+
contract: unknownContract,
91+
mapError: {
92+
source: createStore(42),
93+
fn: () => ({ errorCode: 123 } as const),
94+
},
95+
},
96+
});
97+
98+
expectTypeOf(mutation.finished.failure).toEqualTypeOf<
99+
Event<{
100+
error: { readonly errorCode: 123 };
101+
params: string;
102+
meta: ExecutionMeta;
103+
}>
104+
>();
105+
});
106+
107+
test('without mapError, error type is JsonApiRequestError', () => {
108+
const mutation = createJsonMutation({
109+
params: declareParams<string>(),
110+
request: { url: 'http://api.salo.com', method: 'POST' as const },
111+
response: {
112+
contract: unknownContract,
113+
},
114+
});
115+
116+
expectTypeOf(mutation.finished.failure).toEqualTypeOf<
117+
Event<{
118+
error: JsonApiRequestError;
119+
params: string;
120+
meta: ExecutionMeta;
121+
}>
122+
>();
123+
});
124+
});
125+
});
126+

packages/core/src/mutation/create_json_mutation.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ export function createJsonMutation<
101101
HeadersSource = void,
102102
UrlSource = void,
103103
DataSource = void,
104+
MappedError = JsonApiRequestError,
104105
FailureSource = void,
105106
ValidationSource = void,
106107
>(
@@ -121,14 +122,14 @@ export function createJsonMutation<
121122
>;
122123
mapError?: DynamicallySourcedField<
123124
{ error: JsonApiRequestError; params: Params; headers?: Headers },
124-
unknown,
125+
MappedError,
125126
FailureSource
126127
>;
127128
validate?: Validator<TransformedData, Params, ValidationSource>;
128129
status?: { expected: number | number[] };
129130
};
130131
}
131-
): Mutation<Params, TransformedData, JsonApiRequestError>;
132+
): Mutation<Params, TransformedData, MappedError>;
132133

133134
// params + no mapData
134135
export function createJsonMutation<
@@ -138,6 +139,7 @@ export function createJsonMutation<
138139
QuerySource = void,
139140
HeadersSource = void,
140141
UrlSource = void,
142+
MappedError = JsonApiRequestError,
141143
FailureSource = void,
142144
ValidationSource = void,
143145
>(
@@ -153,14 +155,14 @@ export function createJsonMutation<
153155
contract: Contract<unknown, Data>;
154156
mapError?: DynamicallySourcedField<
155157
{ error: JsonApiRequestError; params: Params; headers?: Headers },
156-
unknown,
158+
MappedError,
157159
FailureSource
158160
>;
159161
validate?: Validator<Data, Params, ValidationSource>;
160162
status?: { expected: number | number[] };
161163
};
162164
}
163-
): Mutation<Params, Data, JsonApiRequestError>;
165+
): Mutation<Params, Data, MappedError>;
164166

165167
// No params + mapData
166168
export function createJsonMutation<
@@ -171,6 +173,7 @@ export function createJsonMutation<
171173
HeadersSource = void,
172174
UrlSource = void,
173175
DataSource = void,
176+
MappedError = JsonApiRequestError,
174177
FailureSource = void,
175178
ValidationSource = void,
176179
>(
@@ -190,14 +193,14 @@ export function createJsonMutation<
190193
>;
191194
mapError?: DynamicallySourcedField<
192195
{ error: JsonApiRequestError; params: void; headers?: Headers },
193-
unknown,
196+
MappedError,
194197
FailureSource
195198
>;
196199
validate?: Validator<TransformedData, void, ValidationSource>;
197200
status?: { expected: number | number[] };
198201
};
199202
}
200-
): Mutation<void, TransformedData, JsonApiRequestError>;
203+
): Mutation<void, TransformedData, MappedError>;
201204

202205
// No params + no mapData
203206
export function createJsonMutation<
@@ -206,6 +209,7 @@ export function createJsonMutation<
206209
QuerySource = void,
207210
HeadersSource = void,
208211
UrlSource = void,
212+
MappedError = JsonApiRequestError,
209213
FailureSource = void,
210214
ValidationSource = void,
211215
>(
@@ -220,14 +224,14 @@ export function createJsonMutation<
220224
contract: Contract<unknown, Data>;
221225
mapError?: DynamicallySourcedField<
222226
{ error: JsonApiRequestError; params: void; headers?: Headers },
223-
unknown,
227+
MappedError,
224228
FailureSource
225229
>;
226230
validate?: Validator<Data, void, ValidationSource>;
227231
status?: { expected: number | number[] };
228232
};
229233
}
230-
): Mutation<void, Data, JsonApiRequestError>;
234+
): Mutation<void, Data, MappedError>;
231235

232236
// -- Implementation --
233237
export function createJsonMutation(config: any): Mutation<any, any, any> {
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { createStore, Event, Store } from 'effector';
2+
import { describe, test, expectTypeOf } from 'vitest';
3+
4+
import { unknownContract } from '../../contract/unknown_contract';
5+
import { declareParams } from '../../remote_operation/params';
6+
import { createJsonQuery } from '../create_json_query';
7+
import { JsonApiRequestError } from '../../fetch/api';
8+
import { ExecutionMeta } from '../../remote_operation/type';
9+
10+
describe('createJsonQuery', () => {
11+
describe('mapError', () => {
12+
test('callback receives correct types', () => {
13+
createJsonQuery({
14+
params: declareParams<string>(),
15+
request: { url: 'http://api.salo.com', method: 'GET' as const },
16+
response: {
17+
contract: unknownContract,
18+
mapError: ({ error, params, headers }) => {
19+
expectTypeOf(error).toEqualTypeOf<JsonApiRequestError>();
20+
expectTypeOf(params).toEqualTypeOf<string>();
21+
expectTypeOf(headers).toEqualTypeOf<Headers | undefined>();
22+
23+
return { code: 'ERROR', message: 'test' };
24+
},
25+
},
26+
});
27+
});
28+
29+
test('callback receives void params when no params declared', () => {
30+
createJsonQuery({
31+
request: { url: 'http://api.salo.com', method: 'GET' as const },
32+
response: {
33+
contract: unknownContract,
34+
mapError: ({ error, params, headers }) => {
35+
expectTypeOf(error).toEqualTypeOf<JsonApiRequestError>();
36+
expectTypeOf(params).toEqualTypeOf<void>();
37+
expectTypeOf(headers).toEqualTypeOf<Headers | undefined>();
38+
39+
return { code: 'ERROR' };
40+
},
41+
},
42+
});
43+
});
44+
45+
test('return type is used for $error store', () => {
46+
const query = createJsonQuery({
47+
params: declareParams<string>(),
48+
request: { url: 'http://api.salo.com', method: 'GET' as const },
49+
response: {
50+
contract: unknownContract,
51+
mapError: () => ({ code: 'ERROR', message: 'test' } as const),
52+
},
53+
});
54+
55+
expectTypeOf(query.$error).toEqualTypeOf<
56+
Store<{ readonly code: 'ERROR'; readonly message: 'test' } | null>
57+
>();
58+
});
59+
60+
test('return type is used for finished.failure event', () => {
61+
const query = createJsonQuery({
62+
params: declareParams<string>(),
63+
request: { url: 'http://api.salo.com', method: 'GET' as const },
64+
response: {
65+
contract: unknownContract,
66+
mapError: () => ({ code: 'ERROR', message: 'test' } as const),
67+
},
68+
});
69+
70+
expectTypeOf(query.finished.failure).toEqualTypeOf<
71+
Event<{
72+
error: { readonly code: 'ERROR'; readonly message: 'test' };
73+
params: string;
74+
meta: ExecutionMeta;
75+
}>
76+
>();
77+
});
78+
79+
test('sourced callback receives source value', () => {
80+
createJsonQuery({
81+
params: declareParams<string>(),
82+
request: { url: 'http://api.salo.com', method: 'GET' as const },
83+
response: {
84+
contract: unknownContract,
85+
mapError: {
86+
source: createStore({ defaultMessage: 'Unknown error' }),
87+
fn: ({ error, params, headers }, source) => {
88+
expectTypeOf(error).toEqualTypeOf<JsonApiRequestError>();
89+
expectTypeOf(params).toEqualTypeOf<string>();
90+
expectTypeOf(headers).toEqualTypeOf<Headers | undefined>();
91+
expectTypeOf(source).toEqualTypeOf<{ defaultMessage: string }>();
92+
93+
return { code: 'ERROR', message: source.defaultMessage };
94+
},
95+
},
96+
},
97+
});
98+
});
99+
100+
test('sourced callback return type is used for $error', () => {
101+
const query = createJsonQuery({
102+
params: declareParams<string>(),
103+
request: { url: 'http://api.salo.com', method: 'GET' as const },
104+
response: {
105+
contract: unknownContract,
106+
mapError: {
107+
source: createStore(42),
108+
fn: () => ({ errorCode: 123 } as const),
109+
},
110+
},
111+
});
112+
113+
expectTypeOf(query.$error).toEqualTypeOf<
114+
Store<{ readonly errorCode: 123 } | null>
115+
>();
116+
});
117+
118+
test('sourced callback return type is used for finished.failure', () => {
119+
const query = createJsonQuery({
120+
params: declareParams<string>(),
121+
request: { url: 'http://api.salo.com', method: 'GET' as const },
122+
response: {
123+
contract: unknownContract,
124+
mapError: {
125+
source: createStore(42),
126+
fn: () => ({ errorCode: 123 } as const),
127+
},
128+
},
129+
});
130+
131+
expectTypeOf(query.finished.failure).toEqualTypeOf<
132+
Event<{
133+
error: { readonly errorCode: 123 };
134+
params: string;
135+
meta: ExecutionMeta;
136+
}>
137+
>();
138+
});
139+
140+
test('without mapError, error type is JsonApiRequestError', () => {
141+
const query = createJsonQuery({
142+
params: declareParams<string>(),
143+
request: { url: 'http://api.salo.com', method: 'GET' as const },
144+
response: {
145+
contract: unknownContract,
146+
},
147+
});
148+
149+
expectTypeOf(query.$error).toEqualTypeOf<
150+
Store<JsonApiRequestError | null>
151+
>();
152+
153+
expectTypeOf(query.finished.failure).toEqualTypeOf<
154+
Event<{
155+
error: JsonApiRequestError;
156+
params: string;
157+
meta: ExecutionMeta;
158+
}>
159+
>();
160+
});
161+
});
162+
});
163+

0 commit comments

Comments
 (0)