Skip to content

Commit 7559613

Browse files
authored
Infer thrown error from expectations
1 parent 8415261 commit 7559613

File tree

3 files changed

+48
-13
lines changed

3 files changed

+48
-13
lines changed

test-types/import-in-cts/throws.cts

+18-3
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,30 @@ class CustomError extends Error {
1212
}
1313

1414
test('throws', t => {
15-
expectType<Error | undefined>(t.throws(() => {}));
15+
const error1 = t.throws(() => {});
16+
expectType<Error | undefined>(error1);
1617
const error2: CustomError | undefined = t.throws(() => {});
1718
expectType<CustomError | undefined>(error2);
1819
expectType<CustomError | undefined>(t.throws<CustomError>(() => {}));
20+
const error3 = t.throws(() => {}, {instanceOf: CustomError});
21+
expectType<CustomError | undefined>(error3);
22+
const error4 = t.throws(() => {}, {is: new CustomError()});
23+
expectType<CustomError | undefined>(error4);
24+
const error5 = t.throws(() => {}, {instanceOf: CustomError, is: new CustomError()});
25+
expectType<CustomError | undefined>(error5);
1926
});
2027

2128
test('throwsAsync', async t => {
22-
expectType<Error | undefined>(await t.throwsAsync(async () => {}));
29+
const error1 = await t.throwsAsync(async () => {});
30+
expectType<Error | undefined>(error1);
2331
expectType<CustomError | undefined>(await t.throwsAsync<CustomError>(async () => {}));
24-
expectType<Error | undefined>(await t.throwsAsync(Promise.reject()));
32+
const error2 = await t.throwsAsync(Promise.reject());
33+
expectType<Error | undefined>(error2);
2534
expectType<CustomError | undefined>(await t.throwsAsync<CustomError>(Promise.reject()));
35+
const error3 = await t.throwsAsync(async () => {}, {instanceOf: CustomError});
36+
expectType<CustomError | undefined>(error3);
37+
const error4 = await t.throwsAsync(async () => {}, {is: new CustomError()});
38+
expectType<CustomError | undefined>(error4);
39+
const error5 = await t.throwsAsync(async () => {}, {instanceOf: CustomError, is: new CustomError()});
40+
expectType<CustomError | undefined>(error5);
2641
});

test-types/module/throws.ts

+18-3
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,30 @@ class CustomError extends Error {
1212
}
1313

1414
test('throws', t => {
15-
expectType<Error | undefined>(t.throws(() => {}));
15+
const error1 = t.throws(() => {});
16+
expectType<Error | undefined>(error1);
1617
const error2: CustomError | undefined = t.throws(() => {});
1718
expectType<CustomError | undefined>(error2);
1819
expectType<CustomError | undefined>(t.throws<CustomError>(() => {}));
20+
const error3 = t.throws(() => {}, {instanceOf: CustomError});
21+
expectType<CustomError | undefined>(error3);
22+
const error4 = t.throws(() => {}, {is: new CustomError()});
23+
expectType<CustomError | undefined>(error4);
24+
const error5 = t.throws(() => {}, {instanceOf: CustomError, is: new CustomError()});
25+
expectType<CustomError | undefined>(error5);
1926
});
2027

2128
test('throwsAsync', async t => {
22-
expectType<Error | undefined>(await t.throwsAsync(async () => {}));
29+
const error1 = await t.throwsAsync(async () => {});
30+
expectType<Error | undefined>(error1);
2331
expectType<CustomError | undefined>(await t.throwsAsync<CustomError>(async () => {}));
24-
expectType<Error | undefined>(await t.throwsAsync(Promise.reject()));
32+
const error2 = await t.throwsAsync(Promise.reject());
33+
expectType<Error | undefined>(error2);
2534
expectType<CustomError | undefined>(await t.throwsAsync<CustomError>(Promise.reject()));
35+
const error3 = await t.throwsAsync(async () => {}, {instanceOf: CustomError});
36+
expectType<CustomError | undefined>(error3);
37+
const error4 = await t.throwsAsync(async () => {}, {is: new CustomError()});
38+
expectType<CustomError | undefined>(error4);
39+
const error5 = await t.throwsAsync(async () => {}, {instanceOf: CustomError, is: new CustomError()});
40+
expectType<CustomError | undefined>(error5);
2641
});

types/assertions.d.cts

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
1-
export type ErrorConstructor = new (...args: any[]) => Error;
1+
export type ErrorConstructor<ErrorType extends Error = Error> = {
2+
new (...args: any[]): ErrorType;
3+
readonly prototype: ErrorType;
4+
}
5+
6+
export type ThrownError<ErrorType extends ErrorConstructor | Error> = ErrorType extends ErrorConstructor ? ErrorType['prototype'] : ErrorType;
27

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

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

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

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

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

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

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

0 commit comments

Comments
 (0)