1.0.1 • Published 5 years ago

@ts-task/utils v1.0.1

Weekly downloads
1
License
MIT
Repository
github
Last release
5 years ago

@ts-task/utils

Npm version Build Status Coverage Status npm bundle size (minified) styled with prettier

Utils functions to use with Task.

API

allProperties

allProperties(object)

allProperties works similar to Task.all but for objects instead of array. It traverses an object whose values are Tasks and returns a new Task of the resolved object. The returned Task can fail with the sum of the individual errors.

import { Task } from '@ts-task/task';
import { allProperties } from '@ts-task/utils';

class CustomError {
}

const obj = {
    name: Task.resolve<string, Error>('Async name'),
    age: Task.resolve<number, CustomError>(29)
};

allProperties(obj).fork(
    // Err can be Error or CustomError
    err => console.error(err)
    // Val is an object, val.name is a string and val.age is a number
    val => console.log(val)
);

caseError

caseError(predicate, errHandler)

caseError takes a predicate (a function to a boolean) and an error handler. If predicate returns true when called with the rejected error, then errHandler is called with the error and it's return value is returned. Else, the rejected error is rejected again.

import { Task } from '@ts-task/task';
import { caseError } from '@ts-task/utils';

// rejectedTask is a Task<never, FooError | BarError>
const rejectedTask = Task.reject(
        Math.random() > 0.5 ?
            new FooError() :
            new BarError()
    );

rejectedTask
    .catch(err =>
        // err is FooError | BarError
        Task.reject(err)
    )
    .catch(
        caseError(
            isInstanceOf(FooError),
            err =>
                // err is a FooError
                Task.resolve('foo ' + err.toString())
        )
    )
    .catch(err =>
        // err is a BarError (since the FooError case was resolved)
        Task.reject(err)
    )
;

Note: have in mind that TypeScript does duck typing checks, hence FooError and BarError should have different properties to let TypeScript infere they are different, since TypeScript has structural typing instead of nominal typing.

isInstanceOf

isInstanceOf(Constructor1, Constructor2, ...) => (instance: any) => instance is Constructor1 | Constructor2 | ...

It is an util function to use with caseError. isInstanceOf takes any number of constructors (or classes) and returns a function that tells us if an object is an instance of any of those constructors. In case it is, it is also typed as well (see type guards).

class Dog {
    bark () {
        return 'WOOF!';
    }
}

class Cat {
    meow () {
        return 'Meeeeeoooooooow';
    }
}

const isDog = isInstanceOf(Dog);

// This example is only for demonstration porpuses.
// I should actually prefer the animals to be polymorphic.
const talk = (animal: Dog | Cat) => {
    if (isDog(animal)) {
        // animal is typed as Dog
        animal.bark();
    }
    else {
        // animal is typed as Cat
        animal.meow();
    }
}

share

task.pipe(share())

As Tasks are lazy, the Task's code isn't executed until it's resolved. But, for the same reason the Task's code is executed each time it is forked (operators - including .map, .chain and .catch methods - do fork the Task). share function is an operator that resolves the Task to that point and returns a Task resolved (or rejected) with that value, so original Task's code is executed only once.

import { Task } from '@ts-task/task';
import { share } from '@ts-task/utils';

const task = new Task<string, never>(resolve => {
    console.log('Task\'s code is called');
    resolve('Foo');
});

const sharedTask = task
    .pipe(share);

sharedTask.fork(err => console.log(err), val => console.log(val));
sharedTask.fork(err => console.log(err), val => console.log(val));
sharedTask.fork(err => console.log(err), val => console.log(val));

// The message "Task's code is called" will be logged only once (even when forking multiple times).

toPromise

toPromise(task)

toPromise function naturally transforms a Task into a Promise.

import { Task } from '@ts-task/task';
import { toPromise } from '@ts-task/utils';

// resolvedTask is a Task<number, never>
const resolvedTask = Task.resolve(9);

// resolvedPromise is Promise<number>
const resolvedPromise = toPromise(resolvedTask);


// rejectedTask is a Task<never, Error>
const rejectedTask = Task.resolve(new Error());

// rejectedPromise is Promise<never>, rejected with Error
const rejectedPromise = toPromise(rejectedTask);

tryCatch

tryCatch(mapFn, handler)

tryCatch works like map but inside a try catch block. If the function throws the handler is executed with the thrown error.

import { Task } from '@ts-task/task';
import { tryCatch } from '@ts-task/utils';

// Try Catch can be useful to type the error
Task.resolve('{some invalid json').pipe(
  tryCatch(
    str => JSON.parse(str),
    err => err as SyntaxError
  )
) // $ExpectType Task<any, UnknownError | SyntaxError>

// Or even wrap it in another object
class ParsingCustomError {
  constructor (public error: SyntaxError) {
    this.message = 'custom message';
  }
}

Task.resolve('{some invalid json').pipe(
  tryCatch(
    str => JSON.parse(str),
    err => new ParsingCustomError(err)
  )
) // $ExpectType Task<any, UnknownError | ParsingCustomError>

Type Helpers

Thanks to the introduction of conditional types in version 2.8 we can create type helpers that let us know the type of the value or possible errors.

import { Task } from '@ts-task/task';
import { TaskValue, TaskError } from '@ts-task/utils';

const t1 = Task.resolve(1);
type T1 = TaskValue<typeof t1>;

// $ExpectType number
const n: T1 = 1;

const e1 = Task.reject('oh no');
type E1 = TaskError<typeof e1>;

// $ExpectType string
const s: E1 = 'oh no';

This can be used in conjunction with other type functions to name the type of a complex expression

type UserWithComments = TaskValue<ReturnType<getUserWithComments>>;

const getUserWithComments = (id: number) =>
    getUser(id).chain(getComments);

Credits

Gonzalo Gluzman💻 📖 ⚠️Hernan Rajchert💻 🎨 📖 🤔 ⚠️

This project follows the all-contributors specification. Contributions of any kind are welcome!

1.1.0-RC.11

5 years ago

0.0.0-10

5 years ago

1.0.1

5 years ago

1.0.0

6 years ago

0.0.1

6 years ago

0.0.0

6 years ago