I'm working on a TypeScript project where I have a generic action handler that can return either a success or error response. The issue is that TypeScript isn't properly inferring the return type in some cases.
Code Example
1
2type ActionResponse<T> = SuccessResponse<T> | ErrorResponse;
3
4
5
6type SuccessResponse<T> = {
7
8 success: true;
9
10 data: T;
11
12};
13
14
15
16type ErrorResponse = {
17
18 success: false;
19
20 error: { message: string };
21
22 status: number;
23
24};
25
26
27
28async function getQuestions(params: SearchParams): Promise<ActionResponse<Question[]>> {
29
30 // ... implementation
31
32}
33
1
2type ActionResponse<T> = SuccessResponse<T> | ErrorResponse;
3
4
5
6type SuccessResponse<T> = {
7
8 success: true;
9
10 data: T;
11
12};
13
14
15
16type ErrorResponse = {
17
18 success: false;
19
20 error: { message: string };
21
22 status: number;
23
24};
25
26
27
28async function getQuestions(params: SearchParams): Promise<ActionResponse<Question[]>> {
29
30 // ... implementation
31
32}
33
Problem
When I call this function, TypeScript sometimes doesn't narrow the type properly:
1
2const result = await getQuestions(params);
3
4if (result.success) {
5
6 // TypeScript should know result.data exists here
7
8 console.log(result.data); // Type error: Property 'data' does not exist
9
10}
11
1
2const result = await getQuestions(params);
3
4if (result.success) {
5
6 // TypeScript should know result.data exists here
7
8 console.log(result.data); // Type error: Property 'data' does not exist
9
10}
11
Question
How can I improve the type definitions to ensure proper type narrowing with discriminated unions in async functions?