Skip to content

Commit 5dffbbe

Browse files
authored
Add static is methods to error types for type narrowing (#12546)
1 parent 28e857f commit 5dffbbe

16 files changed

+254
-13
lines changed

.api-reports/api-report-errors.api.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ export class CombinedGraphQLErrors extends Error {
1414
constructor(result: FetchResult_2<unknown>);
1515
readonly data: Record<string, unknown> | null | undefined;
1616
readonly errors: ReadonlyArray<GraphQLFormattedError>;
17+
static is(error: unknown): error is CombinedGraphQLErrors;
1718
}
1819

1920
// @public
2021
export class CombinedProtocolErrors extends Error {
2122
constructor(protocolErrors: Array<GraphQLFormattedError> | ReadonlyArray<GraphQLFormattedError>);
2223
// (undocumented)
2324
errors: ReadonlyArray<GraphQLFormattedError>;
25+
static is(error: unknown): error is CombinedProtocolErrors;
2426
}
2527

2628
// @public (undocumented)
@@ -43,6 +45,7 @@ export const PROTOCOL_ERRORS_SYMBOL: unique symbol;
4345
export class ServerError extends Error {
4446
// Warning: (ae-forgotten-export) The symbol "ServerErrorOptions" needs to be exported by the entry point index.d.ts
4547
constructor(message: string, options: ServerErrorOptions);
48+
static is(error: unknown): error is ServerError;
4649
response: Response;
4750
result: Record<string, any> | string;
4851
statusCode: number;
@@ -61,6 +64,7 @@ export class ServerParseError extends Error {
6164
// Warning: (ae-forgotten-export) The symbol "ServerParseErrorOptions" needs to be exported by the entry point index.d.ts
6265
constructor(originalParseError: unknown, options: ServerParseErrorOptions);
6366
bodyText: string;
67+
static is(error: unknown): error is ServerParseError;
6468
response: Response;
6569
statusCode: number;
6670
}
@@ -79,6 +83,7 @@ export function toErrorLike(error: unknown): ErrorLike;
7983
// @public
8084
export class UnconventionalError extends Error {
8185
constructor(errorType: unknown);
86+
static is(error: unknown): error is UnconventionalError;
8287
}
8388

8489
// (No @packageDocumentation comment for this package)

.api-reports/api-report.api.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,13 +395,15 @@ export class CombinedGraphQLErrors extends Error {
395395
constructor(result: FetchResult<unknown>);
396396
readonly data: Record<string, unknown> | null | undefined;
397397
readonly errors: ReadonlyArray<GraphQLFormattedError>;
398+
static is(error: unknown): error is CombinedGraphQLErrors;
398399
}
399400

400401
// @public
401402
export class CombinedProtocolErrors extends Error {
402403
constructor(protocolErrors: Array<GraphQLFormattedError> | ReadonlyArray<GraphQLFormattedError>);
403404
// (undocumented)
404405
errors: ReadonlyArray<GraphQLFormattedError>;
406+
static is(error: unknown): error is CombinedProtocolErrors;
405407
}
406408

407409
// Warning: (ae-forgotten-export) The symbol "CombineByTypeName" needs to be exported by the entry point index.d.ts
@@ -2154,6 +2156,7 @@ export const serializeFetchParameter: (p: any, label: string) => string;
21542156
export class ServerError extends Error {
21552157
// Warning: (ae-forgotten-export) The symbol "ServerErrorOptions" needs to be exported by the entry point index.d.ts
21562158
constructor(message: string, options: ServerErrorOptions);
2159+
static is(error: unknown): error is ServerError;
21572160
response: Response;
21582161
result: Record<string, any> | string;
21592162
statusCode: number;
@@ -2172,6 +2175,7 @@ export class ServerParseError extends Error {
21722175
// Warning: (ae-forgotten-export) The symbol "ServerParseErrorOptions" needs to be exported by the entry point index.d.ts
21732176
constructor(originalParseError: unknown, options: ServerParseErrorOptions);
21742177
bodyText: string;
2178+
static is(error: unknown): error is ServerParseError;
21752179
response: Response;
21762180
statusCode: number;
21772181
}
@@ -2337,6 +2341,7 @@ export type TypePolicy = {
23372341
// @public
23382342
export class UnconventionalError extends Error {
23392343
constructor(errorType: unknown);
2344+
static is(error: unknown): error is UnconventionalError;
23402345
}
23412346

23422347
// @public (undocumented)

.changeset/four-countries-clean.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
"@apollo/client": minor
3+
---
4+
5+
Add a static `is` method to error types defined by Apollo Client. `is` makes it simpler to determine whether an error is a specific type, which can be helpful in cases where you'd like to narrow the error type in order to use specific properties from that error.
6+
7+
This change applies to the following error types:
8+
- `CombinedGraphQLErrors`
9+
- `CombinedProtocolErrors`
10+
- `ServerError`
11+
- `ServerParseError`
12+
- `UnconventionalError`
13+
14+
**Example**
15+
16+
```ts
17+
import { CombinedGraphQLErrors } from "@apollo/client";
18+
19+
if (CombinedGraphQLErrors.is(error)) {
20+
console.log(error.message);
21+
error.errors.forEach((graphQLError) => console.log(graphQLError.message))
22+
}
23+
```
24+

.size-limits.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (CJS)": 42972,
3-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production) (CJS)": 38452,
4-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\"": 32914,
5-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production)": 27847
2+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (CJS)": 43100,
3+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production) (CJS)": 38618,
4+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\"": 33026,
5+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production)": 27957
66
}

integration-tests/next/src/libs/apolloClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const APOLLO_STATE_PROP_NAME = "__APOLLO_STATE__";
2020
let apolloClient: ApolloClient;
2121

2222
const errorLink = onError(({ error }) => {
23-
if (error instanceof CombinedGraphQLErrors) {
23+
if (CombinedGraphQLErrors.is(error)) {
2424
error.errors.forEach(({ message, locations, path }) =>
2525
console.log(
2626
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`

src/config/jest/areCombinedGraphQLErrorsEqual.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import type { Tester } from "@jest/expect-utils";
22

3+
import { CombinedGraphQLErrors } from "@apollo/client";
4+
35
export const areCombinedGraphQLErrorsEqual: Tester = function (
46
a,
57
b,
68
customTesters
79
) {
8-
const isACombinedGraphQLErrors = a && a.name === "CombinedGraphQLErrors";
9-
const isBCombinedGraphQLErrors = b && b.name === "CombinedGraphQLErrors";
10+
const isACombinedGraphQLErrors = CombinedGraphQLErrors.is(a);
11+
const isBCombinedGraphQLErrors = CombinedGraphQLErrors.is(b);
1012

1113
if (isACombinedGraphQLErrors && isBCombinedGraphQLErrors) {
1214
return (

src/config/jest/areCombinedProtocolErrorsEqual.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import type { Tester } from "@jest/expect-utils";
22

3+
import { CombinedProtocolErrors } from "@apollo/client";
4+
35
export const areCombinedProtocolErrorsEqual: Tester = function (
46
a,
57
b,
68
customTesters
79
) {
8-
const isACombinedProtocolErrors = a && a.name === "CombinedProtocolErrors";
9-
const isBCombinedProtocolErrors = b && b.name === "CombinedProtocolErrors";
10+
const isACombinedProtocolErrors = CombinedProtocolErrors.is(a);
11+
const isBCombinedProtocolErrors = CombinedProtocolErrors.is(b);
1012

1113
if (isACombinedProtocolErrors && isBCombinedProtocolErrors) {
1214
return (

src/config/jest/areServerErrorsEqual.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import type { Tester } from "@jest/expect-utils";
22

3+
import { ServerError } from "@apollo/client";
4+
35
export const areServerErrorsEqual: Tester = function (a, b, customTesters) {
4-
const isAServerError = a && a.name === "ServerError";
5-
const isBServerError = b && b.name === "ServerError";
6+
const isAServerError = ServerError.is(a);
7+
const isBServerError = ServerError.is(b);
68

79
if (isAServerError && isBServerError) {
810
return (

src/errors/CombinedGraphQLErrors.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@ import type { GraphQLFormattedError } from "graphql";
33
import type { FetchResult } from "@apollo/client";
44
import { getGraphQLErrorsFromResult } from "@apollo/client/utilities";
55

6+
import { brand, isBranded } from "./utils.js";
7+
68
/**
79
* Represents the combined list of GraphQL errors returned from the server in a
810
* GraphQL response.
911
*/
1012
export class CombinedGraphQLErrors extends Error {
13+
/** Determine if an error is a `CombinedGraphQLErrors` instance */
14+
static is(error: unknown): error is CombinedGraphQLErrors {
15+
return isBranded(error, "CombinedGraphQLErrors");
16+
}
17+
1118
/**
1219
* The raw list of GraphQL errors returned in a GraphQL response.
1320
*/
@@ -26,6 +33,7 @@ export class CombinedGraphQLErrors extends Error {
2633
this.data = result.data as Record<string, unknown>;
2734
this.name = "CombinedGraphQLErrors";
2835

36+
brand(this);
2937
Object.setPrototypeOf(this, CombinedGraphQLErrors.prototype);
3038
}
3139
}

src/errors/CombinedProtocolErrors.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import type { GraphQLFormattedError } from "graphql";
22

3+
import { brand, isBranded } from "./utils.js";
4+
35
/**
46
* Fatal transport-level errors returned when executing a subscription using the
57
* multipart HTTP subscription protocol. See the documentation on the
68
* [multipart HTTP protocol for GraphQL Subscriptions](https://www.apollographql.com/docs/graphos/routing/operations/subscriptions/multipart-protocol) for more information on these errors.
79
*/
810
export class CombinedProtocolErrors extends Error {
11+
/** Determine if an error is a `CombinedProtocolErrors` instance */
12+
static is(error: unknown): error is CombinedProtocolErrors {
13+
return isBranded(error, "CombinedProtocolErrors");
14+
}
15+
916
errors: ReadonlyArray<GraphQLFormattedError>;
1017

1118
constructor(
@@ -17,6 +24,7 @@ export class CombinedProtocolErrors extends Error {
1724
this.name = "CombinedProtocolErrors";
1825
this.errors = protocolErrors;
1926

27+
brand(this);
2028
Object.setPrototypeOf(this, CombinedProtocolErrors.prototype);
2129
}
2230
}

0 commit comments

Comments
 (0)