diff --git a/.changeset/warm-pans-cross.md b/.changeset/warm-pans-cross.md new file mode 100644 index 00000000000..c0fe368f89b --- /dev/null +++ b/.changeset/warm-pans-cross.md @@ -0,0 +1,5 @@ +--- +"@smithy/smithy-client": minor +--- + +feat: type check helper method to to know if something is an instance of the ServiceException class diff --git a/packages/smithy-client/src/exceptions.spec.ts b/packages/smithy-client/src/exceptions.spec.ts index b82722e0298..fc591f96a3e 100644 --- a/packages/smithy-client/src/exceptions.spec.ts +++ b/packages/smithy-client/src/exceptions.spec.ts @@ -34,6 +34,57 @@ it("ExceptionOptionType allows specifying message", () => { expect(exception.code).toBe("code"); }); +describe("ServiceException type checking", () => { + const error = new ServiceException({ + name: "Error", + $fault: "client", + $metadata: {}, + }); + + const duckTyped = { + $fault: "server", + $metadata: {}, + }; + + describe("isInstance", () => { + it("should return true for ServiceException instances", () => { + expect(ServiceException.isInstance(error)).toBe(true); + }); + + it("should return true for duck-typed objects", () => { + expect(ServiceException.isInstance(duckTyped)).toBe(true); + }); + + it("should return false for null or undefined", () => { + expect(ServiceException.isInstance(null)).toBe(false); + expect(ServiceException.isInstance(undefined)).toBe(false); + }); + + it("should return false for invalid $fault values", () => { + expect(ServiceException.isInstance({ $fault: "invalid", $metadata: {} })).toBe(false); + }); + + it("should return false for missing properties", () => { + expect(ServiceException.isInstance({ $fault: "client" })).toBe(false); + expect(ServiceException.isInstance({ $metadata: {} })).toBe(false); + }); + }); + + describe("instanceof", () => { + it("should return true for ServiceException instances", () => { + expect(error instanceof ServiceException).toBe(true); + }); + + it("should return true for duck-typed objects", () => { + expect(duckTyped instanceof ServiceException).toBe(true); + }); + + it("should return false for invalid objects", () => { + expect({} instanceof ServiceException).toBe(false); + }); + }); +}); + describe("decorateServiceException", () => { const exception = new ServiceException({ name: "Error", diff --git a/packages/smithy-client/src/exceptions.ts b/packages/smithy-client/src/exceptions.ts index 8ab84cdaf13..298fd40a65a 100644 --- a/packages/smithy-client/src/exceptions.ts +++ b/packages/smithy-client/src/exceptions.ts @@ -37,6 +37,23 @@ export class ServiceException extends Error implements SmithyException, Metadata this.$fault = options.$fault; this.$metadata = options.$metadata; } + + /** + * Checks if a value is an instance of ServiceException (duck typed) + */ + public static isInstance(value: unknown): value is ServiceException { + if (!value) return false; + const candidate = value as ServiceException; + return ( + Boolean(candidate.$fault) && + Boolean(candidate.$metadata) && + (candidate.$fault === "client" || candidate.$fault === "server") + ); + } + + public static [Symbol.hasInstance](instance: unknown) { + return ServiceException.isInstance(instance); + } } /**