Skip to content

Commit

Permalink
feat(isInstanceOf): Added a variadic isInstanceOf that allows you to …
Browse files Browse the repository at this point in the history
…catch errors by class type (#7)

* feat(caseError): caseError now works with predicates instead of classes

* feat(isInstanceOf): added isInstanceOf variadic function

* fix(isInstanceOf): simplified typings of 'isInstanceOf'
  • Loading branch information
dggluz authored and hrajchert committed Aug 27, 2018
1 parent d15a1f2 commit ecb40ad
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 6 deletions.
4 changes: 1 addition & 3 deletions src/case-error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ describe('caseError:', () => {
const task = divideTask(2, 0)

// WHEN: We catch and solve the error
const result = task
.catch(caseError(isDivisionByZeroError, _ => Task.resolve(-1000)))
;
const result = task.catch(caseError(isDivisionByZeroError, _ => Task.resolve(-1000)))
// THEN: The resulting type doesn't have the catched error as a posibility
// and the task is resolved with the catched response
result.fork(jestAssertUntypedNeverCalled(cb), assertFork(cb, n => expect(n).toBe(-1000)))
Expand Down
84 changes: 84 additions & 0 deletions src/is-instance-of.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { isInstanceOf } from './is-instance-of';

describe('isInstanceOf', () => {
class Foo {
Foo = 'Foo';
}

class Bar {
Bar = 'Bar';
}

class Baz {
Baz = 'Baz';
}

describe('instanceOf with only one constructor', () => {
it('An isInstanceOf function returns false when the object is not instance of the constructor', () => {
// GIVEN: an "isInstanceOf" function that checks agains `Foo`
const isInstanceOfFoo = isInstanceOf(Foo);
// ...and an object that is not instance of `Foo`
const bar = new Bar();

// WHEN: calling that "instanceOf" function with that object
const result = isInstanceOfFoo(bar);

// THEN: the result is false
expect(result).toBe(false);
});

it('An isInstanceOf function returns true when the object is instance of the constructor', () => {
// GIVEN: an "isInstanceOf" function that checks agains `Foo`
const isInstanceOfFoo = isInstanceOf(Foo);
// ...and an object that is instance of `Foo`
const foo = new Foo();

// WHEN: calling that "instanceOf" function with that object
const result = isInstanceOfFoo(foo);

// THEN: the result is true
expect(result).toBe(true);
});
});

describe('instanceOf with multiple constructors', () => {
it('An isInstanceOf function returns false when the object is not instance of any of the constructors', () => {
// GIVEN: an "isInstanceOf" function that checks agains `Foo` and `Bar`
const isInstanceOfFooOrBar = isInstanceOf(Foo, Bar);
// ...and an object that is neither instance of `Foo` or `Bar`
const baz = new Baz();

// WHEN: calling that "instanceOf" function with that object
const result = isInstanceOfFooOrBar(baz);

// THEN: the result is false
expect(result).toBe(false);
});

it('An isInstanceOf function returns true when the object is instance of one of the constructors', () => {
// GIVEN: an "isInstanceOf" function that checks agains `Foo`
const isInstanceOfFooOrBar = isInstanceOf(Foo, Bar);
// ...and an object that is instance of `Foo`
const foo = new Foo();

// WHEN: calling that "instanceOf" function with that object
const result = isInstanceOfFooOrBar(foo);

// THEN: the result is true
expect(result).toBe(true);
});

it('An isInstanceOf function returns true when the object is instance of the other constructor', () => {
// GIVEN: an "isInstanceOf" function that checks agains `Foo`
const isInstanceOfFooOrBar = isInstanceOf(Foo, Bar);
// ...and an object that is instance of `Foo`
const bar = new Bar();

// WHEN: calling that "instanceOf" function with that object
const result = isInstanceOfFooOrBar(bar);

// THEN: the result is true
expect(result).toBe(true);
});
});
});
103 changes: 103 additions & 0 deletions src/is-instance-of.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
export type Constructor<T> = { new (...args: any[]): T }

export function isInstanceOf <A, B, C, D, E, F, G, H, I, J> (
constructor1: Constructor<A>,
constructor2: Constructor<B>,
constructor3: Constructor<C>,
constructor4: Constructor<D>,
constructor5: Constructor<E>,
constructor6: Constructor<F>,
constructor7: Constructor<G>,
constructor8: Constructor<H>,
constructor9: Constructor<I>,
constructor10: Constructor<J>
):
(instance: any) => instance is A | B | C | D | E | F | G | H | I | J
;
export function isInstanceOf <A, B, C, D, E, F, G, H, I> (
constructor1: Constructor<A>,
constructor2: Constructor<B>,
constructor3: Constructor<C>,
constructor4: Constructor<D>,
constructor5: Constructor<E>,
constructor6: Constructor<F>,
constructor7: Constructor<G>,
constructor8: Constructor<H>,
constructor9: Constructor<I>
):
(instance: any) => instance is A | B | C | D | E | F | G | H | I
;
export function isInstanceOf <A, B, C, D, E, F, G, H> (
constructor1: Constructor<A>,
constructor2: Constructor<B>,
constructor3: Constructor<C>,
constructor4: Constructor<D>,
constructor5: Constructor<E>,
constructor6: Constructor<F>,
constructor7: Constructor<G>,
constructor8: Constructor<H>
):
(instance: any) => instance is A | B | C | D | E | F | G | H
;
export function isInstanceOf <A, B, C, D, E, F, G> (
constructor1: Constructor<A>,
constructor2: Constructor<B>,
constructor3: Constructor<C>,
constructor4: Constructor<D>,
constructor5: Constructor<E>,
constructor6: Constructor<F>,
constructor7: Constructor<G>
):
(instance: any) => instance is A | B | C | D | E | F | G
;
export function isInstanceOf <A, B, C, D, E, F> (
constructor1: Constructor<A>,
constructor2: Constructor<B>,
constructor3: Constructor<C>,
constructor4: Constructor<D>,
constructor5: Constructor<E>,
constructor6: Constructor<F>
):
(instance: any) => instance is A | B | C | D | E | F
;
export function isInstanceOf <A, B, C, D, E> (
constructor1: Constructor<A>,
constructor2: Constructor<B>,
constructor3: Constructor<C>,
constructor4: Constructor<D>,
constructor5: Constructor<E>
):
(instance: any) => instance is A | B | C | D | E
;
export function isInstanceOf <A, B, C, D> (
constructor1: Constructor<A>,
constructor2: Constructor<B>,
constructor3: Constructor<C>,
constructor4: Constructor<D>
):
(instance: any) => instance is A | B | C | D
;
export function isInstanceOf <A, B, C> (
constructor1: Constructor<A>,
constructor2: Constructor<B>,
constructor3: Constructor<C>
):
(instance: any) => instance is A | B | C
;
export function isInstanceOf <A, B> (
constructor1: Constructor<A>,
constructor2: Constructor<B>
):
(instance: any) => instance is A | B
;
export function isInstanceOf <A> (
constructor1: Constructor<A>
):
(instance: any) => instance is A
;
export function isInstanceOf <A> (...constructors: Constructor<A>[]) {
return (instance: any): instance is A =>
constructors.some(aConstructor =>
instance instanceof aConstructor
);
}
7 changes: 4 additions & 3 deletions src/ts-task-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './case-error'
export * from './to-promise'
export * from './operators/share'
export * from './case-error';
export * from './to-promise';
export * from './operators/share';
export * from './is-instance-of';
60 changes: 60 additions & 0 deletions test/types/is-instance-of-and-case-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Task } from '@ts-task/task';
import { caseError, isInstanceOf } from '@ts-task/utils';

// We will test typings when using `isInstanceOf` together with `caseError`

// We got some classes
class Foo {
Foo = 'Foo';
}

class Bar {
Bar = 'Bar';
}

class Baz {
Baz = 'Baz';
}

// And a task that is rejected with all those classes
const task = Task
.resolve(9)
.chain(x => x > 0 ? Task.resolve(x) : Task.reject(new Foo()))
.chain(x => x > 0 ? Task.resolve(x) : Task.reject(new Bar()))
.chain(x => x > 0 ? Task.resolve(x) : Task.reject(new Baz()))
;

task; // $ExpectType Task<number, UncaughtError | Foo | Bar | Baz>

// If we handle the Foo errors
const fooHandled = task
.catch(caseError(
isInstanceOf(Foo),
_ => Task.resolve(0)
))
;

// ...we get a Task that is NOT rejected with the Foo errors
fooHandled; // $ExpectType Task<number, UncaughtError | Bar | Baz>

// If we handle the Foo and the Bar errors
const fooAndBarHandled = task
.catch(caseError(
isInstanceOf(Foo, Bar),
_ => Task.resolve(0)
))
;

// ...we get a Task that is neither rejected with the Foo or the Bar errors
fooAndBarHandled; // $ExpectType Task<number, UncaughtError | Baz>

// If we transform that last Task's Baz error into Bar error
const bazTransformedToBar = fooAndBarHandled
.catch(caseError(
isInstanceOf(Baz),
_ => Task.reject(new Bar())
))
;

// We get a Task whose error is Bar (or UncaughtError)
bazTransformedToBar; // $ExpectType Task<number, UncaughtError | Bar>
53 changes: 53 additions & 0 deletions test/types/is-instance-of.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { isInstanceOf } from '@ts-task/utils';

// In this file we will check "isInstanceOf" functions inference.

class Foo {
Foo = 'Foo';
}

class Bar {
Bar = 'Bar';
}

const foo = new Foo();
const bar = new Bar();

const isInstanceOfFoo = isInstanceOf(Foo);
const isInstanceOfFooOrBar = isInstanceOf(Foo, Bar);

// isInstanceOfFoo inference on foo
if (isInstanceOfFoo(foo)) {
// foo is Foo (duh!)
foo; // $ExpectType Foo
} else {
// foo should never be "not Foo"
foo; // $ExpectType never
}

// isInstanceOfFoo inference on bar
if (isInstanceOfFoo(bar)) {
// Someway bar is not only Bar, but also Foo
bar; // $ExpectType Bar & Foo
} else {
// bar is Bar (duh!)
bar; // $ExpectType Bar
}

// isInstanceOfFooOrBar inference on foo
if (isInstanceOfFooOrBar(foo)) {
// foo is only Foo
foo; // $ExpectType Foo
} else {
// foo should never be "not Foo"
foo; // $ExpectType never
}

// isInstanceOfFooOrBar inference on bar
if (isInstanceOfFooOrBar(bar)) {
// boo is only Bar
bar; // $ExpectType Bar
} else {
// bar should never be "not Bar"
bar; // $ExpectType never
}

0 comments on commit ecb40ad

Please sign in to comment.