Skip to content

Commit

Permalink
fix(case error): Replaced conditional type with Exclude
Browse files Browse the repository at this point in the history
* fix(caseError): fixed caseError typings (now works with non-compatible errors and with Tasks errores with only one type)

* fix(caseError): improve caseError typings (fix #2)

* chore(caseError): removed debugging code
  • Loading branch information
dggluz authored and hrajchert committed Aug 27, 2018
1 parent 1d3d8ac commit a5e47d0
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 23 deletions.
29 changes: 17 additions & 12 deletions src/case-error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { assertFork, jestAssertNever, jestAssertUntypedNeverCalled } from './tes
import { caseError } from './case-error';

class DivisionByZeroError extends Error {
type = 'DivisionByZeroError';
private type = 'DivisionByZeroError';
}

class DontLikePairsError extends Error {
type = 'IDontLikePairsError';
class DontLikeEvenNumbersError extends Error {
type = 'DontLikeEvenNumbersError';

constructor (public evenNumber: number) {
super(`Ugh! ${evenNumber} is an even number!`);
}
}

class NewTypeOfError extends Error {
Expand All @@ -23,9 +27,9 @@ function divideTask(numerator: number, denominator: number): Task<number, Divisi
}
}

function rejectPairsTaks(n: number): Task<number, DontLikePairsError> {
function rejectPairsTaks(n: number): Task<number, DontLikeEvenNumbersError> {
if (n % 2 === 0) {
return Task.reject(new DontLikePairsError())
return Task.reject(new DontLikeEvenNumbersError(n))
} else {
return Task.resolve(n)
}
Expand Down Expand Up @@ -64,7 +68,7 @@ describe('caseError:', () => {
})

it('Should leave untouched an unmatched error', cb => {
// GIVEN: A task that fails with IDontLikePairsError
// GIVEN: A task that fails with DontLikeEvenNumbersError
const task = divideAndRejectPairs(4, 2)

// WHEN: We catch the wrong exception trying to reject with a new error
Expand All @@ -75,7 +79,7 @@ describe('caseError:', () => {
// the resulting type has the new rejected type as a posibility
// and the task is rejected with the original error
result.fork(
assertFork(cb, err => expect(err).toBeInstanceOf(DontLikePairsError)),
assertFork(cb, err => expect(err).toBeInstanceOf(DontLikeEvenNumbersError)),
jestAssertUntypedNeverCalled(cb)
)
})
Expand All @@ -93,16 +97,17 @@ describe('caseError:', () => {
)
)
.catch(
caseError(DontLikePairsError, _ =>
Task.resolve('Could not compute: IDontLikePairsError ocurred')
caseError(DontLikeEvenNumbersError, _ =>
Task.resolve('Could not compute: DontLikeEvenNumbersError ocurred')
)
)
.catch(
caseError(UncaughtError, err => Task.resolve(`Could not compute: UncaughtError ${err}`))
)
;
// THEN: The resulting type doesn't have the catched errors
// and the task is resolved with the mapped answer
result.fork(jestAssertNever(cb), assertFork(cb, s => expect(s).toBe('The result is 5')))
result.fork(jestAssertNever(cb), assertFork(cb, s => expect(s).toBe('The result is 5')));
})

it('Should not compile when trying to catch an error that isnt throwed', cb => {
Expand All @@ -112,8 +117,8 @@ describe('caseError:', () => {
// WHEN: We catch an imposible exception
const result = task.catch(
caseError(
DontLikePairsError, // TODO: It would be nice to see this fail compilation as it is not possible that
// task fails with IDontLikePairsError
DontLikeEvenNumbersError, // TODO: It would be nice to see this fail compilation as it is not possible that
// task fails with DontLikeEvenNumbersError
_ => Task.resolve(0)
)
)
Expand Down
22 changes: 11 additions & 11 deletions src/case-error.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { Task } from '@ts-task/task'
import { Task, UncaughtError } from '@ts-task/task'

export type Constructor<T> = { new (...args: any[]): T }

export type IErrorHandler<E, TResult, EResult> = (err: E) => Task<TResult, EResult>
export type ErrorHandler<ErrorToHandle, TResult, EResult> = (err: ErrorToHandle) => Task<TResult, EResult>

export function caseError<E, TResult, EResult>(
errorType: Constructor<E>,
errorHandler: IErrorHandler<E, TResult, EResult>
export function caseError<ErrorToHandle, TResult, EResult>(
ErrorType: Constructor<ErrorToHandle>,
errorHandler: ErrorHandler<ErrorToHandle, TResult, EResult>
) {
return function<RE>(
err: RE
): RE extends E ? Task<TResult, EResult> : Task<TResult, RE | EResult> {
return function <InputError> (
err: InputError | ErrorToHandle
): Task<TResult, EResult | Exclude<InputError, ErrorToHandle>> {
// If the error is of the type we are looking for (E)
if (err instanceof errorType) {
if (err instanceof ErrorType) {
// Transform the error
return errorHandler(err) as any
return errorHandler(err);
} else {
// If not, leave as it is
return Task.reject(err) as any
return Task.reject(err as Exclude<InputError, ErrorToHandle>);
}
}
}

0 comments on commit a5e47d0

Please sign in to comment.