javascript - Hvordan håndtere NodeJS-feil i TypeScript?
Jeg prøver å håndtere unntak kastet av NodeJS ved bruk av TypeScript.
For eksempel, hvis jeg får et ECONNREFUSED-unntak, er det av typen SystemError.
Men akkurat nå, fordi err er av typen any, kan jeg bare gjøre dette
redisClient.on('error', (err) => {
console.log('Error: ', err);
});
Men jeg vil gjerne avgrense feiltypen, lik dette:
redisClient.on('error', (err) => {
if (err instanceof SystemError) {
if(err.code === 'ECONNREFUSED ') {
throw new Error('Connection refused');
}
}
throw err;
});
Men dessverre er SystemError ikke eksportert i Node.
Jeg fant faktisk en diskusjon på GitHub fra for 4 år siden om NodeJS.ErrnoException, men det ser ut til at den ble fjernet.
Er det for øyeblikket mulig i NodeJS?
Svar #1
Merk: Dette diskuteres også på GitHub-issue nodejs/node#46869 — Export SystemError
Jeg trengte å skille systemfeil i typesystemet i noen tidligere prosjekter og fant ingen pakker som gjorde det på en tilfredsstillende måte, så jeg skrev en enkel, utvidbar modul som jeg har brukt siden da — den definerer noen grensesnitt basert på informasjon i Node.js-dokumentasjonen (lenker til kilden inkludert) og type guards som bruker disse grensesnittene:
system_error.ts:
import { getSystemErrorMap } from "node:util";
import { isNativeError } from "node:util/types";
/** https://nodejs.org/docs/latest-v20.x/api/errors.html#class-systemerror */
export interface BaseSystemError<Code extends string = string> extends Error {
/** The string error code */
code: Code;
/** The system-provided error number */
errno: number;
/** A system-provided human-readable description of the error */
message: string;
/** The name of the system call that triggered the error */
syscall: string;
}
/** https://nodejs.org/docs/latest-v20.x/api/errors.html#class-systemerror */
export interface SystemError<Code extends string = string>
extends BaseSystemError<Code> {
/** If present, the address to which a network connection failed */
address?: string;
/** If present, the file path destination when reporting a file system error */
dest?: string;
/** If present, extra details about the error condition */
info?: Record<string, unknown>;
/** If present, the file path when reporting a file system error */
path?: string;
/** If present, the network connection port that is not available */
port?: number;
}
// Memo (lazily initialized):
// https://nodejs.org/docs/latest-v20.x/api/util.html#utilgetsystemerrormap
let systemErrorMap: ReturnType<typeof getSystemErrorMap> | undefined;
type JsType =
| "bigint"
| "boolean"
| "function"
| "number"
| "object"
| "string"
| "symbol"
| "undefined";
export function isSystemError(value: unknown): value is SystemError {
// https://nodejs.org/docs/latest-v20.x/api/util.html#utiltypesisnativeerrorvalue
if (!isNativeError(value)) return false;
for (const [key, jsType] of [
["code", "string"],
["errno", "number"],
["syscall", "string"],
] satisfies [keyof SystemError, JsType][]) {
if (typeof (value as SystemError)[key] !== jsType) return false;
}
systemErrorMap ??= getSystemErrorMap();
return systemErrorMap.has((value as SystemError).errno);
}
export function isSystemErrorWithCode<T extends string>(
value: unknown,
code: T,
): value is SystemError<T> {
return isSystemError(value) && value.code === code;
}
// You can also define type guards for any specific system errors that you want to discriminate…
// https://nodejs.org/docs/latest-v20.x/api/errors.html#common-system-errors
export function isConnectionRefusedError(
value: unknown,
): value is SystemError<"ECONNREFUSED"> {
return isSystemErrorWithCode(value, "ECONNREFUSED");
}
export function isNotFoundError(
value: unknown,
): value is SystemError<"ENOENT"> {
return isSystemErrorWithCode(value, "ENOENT");
}
// …etc.
Du kan bruke det i koden din slik:
import { isConnectionRefusedError } from "./system_error"
function example() {
try {
// statements…
} catch (cause) {
if (isConnectionRefusedError(cause)) {
cause
//^? SystemError<"ECONNREFUSED">
cause.code
// ^? BaseSystemError<"ECONNREFUSED">.code: "ECONNREFUSED"
}
}
}
// or the Redis example in your question:
redisClient.on("error", (err) => {
if (isConnectionRefusedError(err)) {
// It's a "connection refused" error; do something…
} else {
// do something different…
}
});
Kode i TypeScript-lekeplassen
Svar #2
Vennligst prøv dette for å håndtere feil i Typescript:
interface NodeSystemError extends Error {
code?: string;
errno?: number;
syscall?: string;
path?: string;
address?: string;
port?: number;
}
redisClient.on('error', (err) => {
const error = err as NodeSystemError;
if (error.code === 'ECONNREFUSED') {
console.error('Connection refused:', error);
throw new Error('Redis connection refused');
} else if (error.code === 'EHOSTUNREACH') {
console.error('Host unreachable:', error);
throw new Error('Redis host unreachable');
} else {
console.error('Unhandled error:', error);
throw error;
}
});
vi kan også bruke instanceof
redisClient.on('error', (err) => {
if (err instanceof Error && (err as any).code === 'ECONNREFUSED') {
console.error('Connection refused:', err);
throw new Error('Redis connection refused');
} else if (err instanceof Error && (err as any).code === 'EHOSTUNREACH') {
console.error('Host unreachable:', err);
throw new Error('Redis host unreachable');
} else {
console.error('Unhandled error:', err);
throw err;
}
});
