🔨wrap

wrap can secure your functions. This means even if your functions throw errors, it will still be safe to call them without worrying that they will cause your program to crash.

A simple example of wrapping a synchronous function

import { wrap } from 'unwrapit'

const tryParseJson = wrap(() => JSON.parse(`{"package": "unwrapit!"}`))
const json = tryParseJson()
if (!json.ok) {
    return json.error
}
console.log(json.value)

If you are sure that the wrapped function won't panic anyway, or just for debug usage, you can call unwrap method to force unwrap the value.

tryParseJson().unwrap()

This will unwrap the value without checking whether it will throw an error or not.

You can wrap an asynchronous function as well.

import {wrap} from 'unwrapit'

const fetchWrapper = wrap(fetch)
const ret = (await fetchWrapper('www.google.com')).unwrap()
const text = await ret.text()

Note that you need to await or call then to resolve the Promise result first because we don't know if the function will throw errors or not without executing it.

Type

wrap will infer the given function type and return it as is except wrapping the return type of the given function into Result.

declare function fn(a: number): string

const f = wrap(fn)
//    ^? const f: (a: number) => Result<string, unknown>

For asynchronous functions, the return type looks like below

declare function fn(a: number): Promise<string>

const f = wrap(fn)
//    ^? const f: (a: number) => Promise<Result<string, unknown>>

Specify Error Type

By default, wrap won't know what error type the function is going to throw, so it will infer it as unknown, so that you can cast it when you are handling it. However, if you are developing a common function that want to share with others, you probably need to specify the specific error type. With the wrap function type definition, you can specify the error type easily.

Let's say we have a function with a signature below

declare function fn(a: number): Promise<string>
const f = wrap(fn)

It will infer the wrapped type like below

const f: (a: number) => Promise<Result<string, unknown>>

We want to specify the error type as Error. Just do below

import {wrap} from 'unwrapit'

const f = wrap<Error, typeof fn>(fn)

Now the inferred type will be

const f: (a: number) => Promise<Result<string, Error>>

Specify Both Error & Return Type

Sometimes, the function we wrapped may return any, we want to narrow down the return type to make it more precise. You may think of casting the function to be wrapped to the desired signature. Well, with wrap function type, we can just specify the return type like below

declare function fn(a: number): Promise<any>
const f = wrap<Error, typeof fn, Promise<string>>(fn)
//    ^? const f: (a: number) => Promise<Result<string, Error>>

Note that the given returned type should be a subtype of the function's return type, otherwise, TypeScript will complain.

declare function fn(a: number): string
const f = wrap<Error, typeof fn, number>(fn)
//                               ^? Type 'number' does not satisfy the constraint 'string'.

Last updated