Skip to content

Commit

Permalink
Infer thrown error from expectations
Browse files Browse the repository at this point in the history
  • Loading branch information
tao-cumplido authored Feb 6, 2023
1 parent 8415261 commit 7559613
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 13 deletions.
21 changes: 18 additions & 3 deletions test-types/import-in-cts/throws.cts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,30 @@ class CustomError extends Error {
}

test('throws', t => {
expectType<Error | undefined>(t.throws(() => {}));
const error1 = t.throws(() => {});
expectType<Error | undefined>(error1);
const error2: CustomError | undefined = t.throws(() => {});
expectType<CustomError | undefined>(error2);
expectType<CustomError | undefined>(t.throws<CustomError>(() => {}));
const error3 = t.throws(() => {}, {instanceOf: CustomError});
expectType<CustomError | undefined>(error3);
const error4 = t.throws(() => {}, {is: new CustomError()});
expectType<CustomError | undefined>(error4);
const error5 = t.throws(() => {}, {instanceOf: CustomError, is: new CustomError()});
expectType<CustomError | undefined>(error5);
});

test('throwsAsync', async t => {
expectType<Error | undefined>(await t.throwsAsync(async () => {}));
const error1 = await t.throwsAsync(async () => {});
expectType<Error | undefined>(error1);
expectType<CustomError | undefined>(await t.throwsAsync<CustomError>(async () => {}));
expectType<Error | undefined>(await t.throwsAsync(Promise.reject()));
const error2 = await t.throwsAsync(Promise.reject());
expectType<Error | undefined>(error2);
expectType<CustomError | undefined>(await t.throwsAsync<CustomError>(Promise.reject()));
const error3 = await t.throwsAsync(async () => {}, {instanceOf: CustomError});
expectType<CustomError | undefined>(error3);
const error4 = await t.throwsAsync(async () => {}, {is: new CustomError()});
expectType<CustomError | undefined>(error4);
const error5 = await t.throwsAsync(async () => {}, {instanceOf: CustomError, is: new CustomError()});
expectType<CustomError | undefined>(error5);
});
21 changes: 18 additions & 3 deletions test-types/module/throws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,30 @@ class CustomError extends Error {
}

test('throws', t => {
expectType<Error | undefined>(t.throws(() => {}));
const error1 = t.throws(() => {});
expectType<Error | undefined>(error1);
const error2: CustomError | undefined = t.throws(() => {});
expectType<CustomError | undefined>(error2);
expectType<CustomError | undefined>(t.throws<CustomError>(() => {}));
const error3 = t.throws(() => {}, {instanceOf: CustomError});
expectType<CustomError | undefined>(error3);
const error4 = t.throws(() => {}, {is: new CustomError()});
expectType<CustomError | undefined>(error4);
const error5 = t.throws(() => {}, {instanceOf: CustomError, is: new CustomError()});
expectType<CustomError | undefined>(error5);
});

test('throwsAsync', async t => {
expectType<Error | undefined>(await t.throwsAsync(async () => {}));
const error1 = await t.throwsAsync(async () => {});
expectType<Error | undefined>(error1);
expectType<CustomError | undefined>(await t.throwsAsync<CustomError>(async () => {}));
expectType<Error | undefined>(await t.throwsAsync(Promise.reject()));
const error2 = await t.throwsAsync(Promise.reject());
expectType<Error | undefined>(error2);
expectType<CustomError | undefined>(await t.throwsAsync<CustomError>(Promise.reject()));
const error3 = await t.throwsAsync(async () => {}, {instanceOf: CustomError});
expectType<CustomError | undefined>(error3);
const error4 = await t.throwsAsync(async () => {}, {is: new CustomError()});
expectType<CustomError | undefined>(error4);
const error5 = await t.throwsAsync(async () => {}, {instanceOf: CustomError, is: new CustomError()});
expectType<CustomError | undefined>(error5);
});
19 changes: 12 additions & 7 deletions types/assertions.d.cts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
export type ErrorConstructor = new (...args: any[]) => Error;
export type ErrorConstructor<ErrorType extends Error = Error> = {
new (...args: any[]): ErrorType;
readonly prototype: ErrorType;
}

export type ThrownError<ErrorType extends ErrorConstructor | Error> = ErrorType extends ErrorConstructor ? ErrorType['prototype'] : ErrorType;

/** Specify one or more expectations the thrown error must satisfy. */
export type ThrowsExpectation = {
export type ThrowsExpectation<ErrorType extends ErrorConstructor | Error> = {
/** The thrown error must have a code that equals the given string or number. */
code?: string | number;

/** The thrown error must be an instance of this constructor. */
instanceOf?: ErrorConstructor;
instanceOf?: ErrorType extends ErrorConstructor ? ErrorType : ErrorType extends Error ? ErrorConstructor<ErrorType> : never;

/** The thrown error must be strictly equal to this value. */
is?: Error;
is?: ErrorType extends ErrorConstructor ? ErrorType['prototype'] : ErrorType;

/** The thrown error must have a message that equals the given string, or matches the regular expression. */
message?: string | RegExp | ((message: string) => boolean);
Expand Down Expand Up @@ -293,7 +298,7 @@ export type ThrowsAssertion = {
* Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value.
* The error must satisfy all expectations. Returns undefined when the assertion fails.
*/
<ThrownError extends Error>(fn: () => any, expectations?: ThrowsExpectation, message?: string): ThrownError | undefined;
<ErrorType extends ErrorConstructor | Error>(fn: () => any, expectations?: ThrowsExpectation<ErrorType>, message?: string): ThrownError<ErrorType> | undefined;

/** Skip this assertion. */
skip(fn: () => any, expectations?: any, message?: string): void;
Expand All @@ -304,14 +309,14 @@ export type ThrowsAsyncAssertion = {
* Assert that the async function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error
* value. Returns undefined when the assertion fails. You must await the result. The error must satisfy all expectations.
*/
<ThrownError extends Error>(fn: () => PromiseLike<any>, expectations?: ThrowsExpectation, message?: string): Promise<ThrownError | undefined>;
<ErrorType extends ErrorConstructor | Error>(fn: () => PromiseLike<any>, expectations?: ThrowsExpectation<ErrorType>, message?: string): Promise<ThrownError<ErrorType> | undefined>;

/**
* Assert that the promise rejects with [an error](https://www.npmjs.com/package/is-error). If so, returns the
* rejection reason. Returns undefined when the assertion fails. You must await the result. The error must satisfy all
* expectations.
*/
<ThrownError extends Error>(promise: PromiseLike<any>, expectations?: ThrowsExpectation, message?: string): Promise<ThrownError | undefined>;
<ErrorType extends ErrorConstructor | Error>(promise: PromiseLike<any>, expectations?: ThrowsExpectation<ErrorType>, message?: string): Promise<ThrownError<ErrorType> | undefined>;

/** Skip this assertion. */
skip(thrower: any, expectations?: any, message?: string): void;
Expand Down

0 comments on commit 7559613

Please sign in to comment.