Type-Safe Errors for JS & TypeScript

Overview

NeverThrow 🙅

Build Status

Package Size

Description

Encode failure into your program.

This package contains a Result type that represents either success (Ok) or failure (Err).

For asynchronous tasks, neverthrow offers a ResultAsync class which wraps a Promise > and gives you the same level of expressivity and control as a regular Result .

ResultAsync is thenable meaning it behaves exactly like a native Promise ... except you have access to the same methods that Result provides without having to await or .then the promise! Check out the wiki for examples and best practices.

Need to see real-life examples of how to leverage this package for error handling? See this repo: https://github.com/parlez-vous/server

Table Of Contents

Installation

> npm install neverthrow

Top-Level API

neverthrow exposes the following:

  • ok convenience function to create an Ok variant of Result
  • err convenience function to create an Err variant of Result
  • Ok class and type
  • Err class and type
  • Result Type as well as namespace / object from which to call Result.fromThrowable
  • ResultAsync class
  • okAsync convenience function to create a ResultAsync containing an Ok type Result
  • errAsync convenience function to create a ResultAsync containing an Err type Result
  • combine utility function that allows you to turn Result [] into Result , or a ResultAsync [] into ResultAsync (just like Promise.all)
import {
  ok,
  Ok,
  err,
  Err,
  Result,
  okAsync,
  errAsync,
  ResultAsync,
  combine,
  fromThrowable,
  fromPromise,
  fromSafePromise,
} from 'neverthrow'

Check out the wiki for help on how to make the most of neverthrow.

If you find this package useful, please consider sponsoring me or simply buying me a coffee!


API Documentation

Synchronous API (Result)

ok

Constructs an Ok variant of Result

Signature:

ok<T, E>(value: T): Ok<T, E> { ... }

Example:

import { ok } from 'neverthrow'

const myResult = ok({ myData: 'test' }) // instance of `Ok`

myResult.isOk() // true
myResult.isErr() // false

⬆️ Back to top


err

Constructs an Err variant of Result

Signature:

err<T, E>(error: E): Err<T, E> { ... }

Example:

import { err } from 'neverthrow'

const myResult = err('Oh noooo') // instance of `Err`

myResult.isOk() // false
myResult.isErr() // true

⬆️ Back to top


Result.isOk (method)

Returns true if the result is an Ok variant

Signature:

isOk(): boolean { ... }

⬆️ Back to top


Result.isErr (method)

Returns true if the result is an Err variant

Signature:

isErr(): boolean { ... }

⬆️ Back to top


Result.map (method)

Maps a Result to Result by applying a function to a contained Ok value, leaving an Err value untouched.

This function can be used to compose the results of two functions.

Signature:

class Result<T, E> {
  map<U>(callback: (value: T) => U): Result<U, E> { ... }
}

Example:

const { getLines } from 'imaginary-parser'
// ^ assume getLines has the following signature:
// getLines(str: string): Result
   
    
     , Error>
    
   

// since the formatting is deemed correct by `getLines`
// then it means that `linesResult` is an Ok
// containing an Array of strings for each line of code
const linesResult = getLines('1\n2\n3\n4\n')

// this Result now has a Array
   
     inside it
   
const newResult = linesResult.map(
  (arr: Array<string>) => arr.map(parseInt)
)

newResult.isOk() // true

⬆️ Back to top


Result.mapErr (method)

Maps a Result to Result by applying a function to a contained Err value, leaving an Ok value untouched.

This function can be used to pass through a successful result while handling an error.

Signature:

class Result<T, E> {
  mapErr<F>(callback: (error: E) => F): Result<T, F> { ... }
}

Example:

import { parseHeaders } 'imaginary-http-parser'
// imagine that parseHeaders has the following signature:
// parseHeaders(raw: string): Result
   

const rawHeaders = 'nonsensical gibberish and badly formatted stuff'

const parseResult = parseHeaders(rawHeaders)

parseResult.mapErr(parseError => {
  res.status(400).json({
    error: parseError
  })
})

parseResult.isErr() // true

⬆️ Back to top


Result.unwrapOr (method)

Unwrap the Ok value, or return the default if there is an Err

Signature:

class Result<T, E> {
  unwrapOr<T>(value: T): T { ... }
}

Example:

const myResult = err('Oh noooo')

const multiply = (value: number): number => value * 2

const unwrapped: number = myResult.map(multiply).unwrapOr(10)

⬆️ Back to top


Result.andThen (method)

Same idea as map above. Except you must return a new Result.

The returned value will be a Result. As of v4.1.0-beta, you are able to return distinct error types (see signature below). Prior to v4.1.0-beta, the error type could not be distinct.

This is useful for when you need to do a subsequent computation using the inner T value, but that computation might fail.

Additionally, andThen is really useful as a tool to flatten a Result , E1> into a Result (see example below).

Signature:

class Result<T, E> {
  // Note that the latest version lets you return distinct errors as well.
  // If the error types (E and F) are the same (like `string | string`)
  // then they will be merged into one type (`string`)
  andThen<U, F>(
    callback: (value: T) => Result<U, F>
  ): Result<U, E | F> { ... }
}

Example 1: Chaining Results

import { err, ok } from 'neverthrow'

const sq = (n: number): Result<number, number> => ok(n ** 2)

ok(2)
  .andThen(sq)
  .andThen(sq) // Ok(16)

ok(2)
  .andThen(sq)
  .andThen(err) // Err(4)

ok(2)
  .andThen(err)
  .andThen(sq) // Err(2)

err(3)
  .andThen(sq)
  .andThen(sq) // Err(3)

Example 2: Flattening Nested Results

// It's common to have nested Results
const nested = ok(ok(1234))

// notNested is a Ok(1234)
const notNested = nested.andThen((innerResult) => innerResult)

⬆️ Back to top


Result.asyncAndThen (method)

Same idea as andThen above, except you must return a new ResultAsync.

The returned value will be a ResultAsync.

Signature:

class Result<T, E> {
  asyncAndThen<U, F>(
    callback: (value: T) => ResultAsync<U, F>
  ): ResultAsync<U, E | F> { ... }
}

⬆️ Back to top


Result.orElse (method)

Takes an Err value and maps it to a Result . This is useful for error recovery.

Signature:

class Result<T, E> {
  orElse<A>(
    callback: (error: E) => Result<T, A>
  ): Result<T, A> { ... }
}

Example:

enum DatabaseError {
  PoolExhausted = 'PoolExhausted',
  NotFound = 'NotFound',
}

const dbQueryResult: Result<string, DatabaseError> = err(DatabaseError.NotFound)

const updatedQueryResult = dbQueryResult.orElse((dbError) =>
  dbError === DatabaseError.NotFound
    ? ok('User does not exist') // error recovery branch: ok() must be called with a value of type string
    //
    //
    // err() can be called with a value of any new type that you want
    // it could also be called with the same error value
    //     
    //     err(dbError)
    : err(500) 
)

⬆️ Back to top


Result.match (method)

Given 2 functions (one for the Ok variant and one for the Err variant) execute the function that matches the Result variant.

Match callbacks do not necessitate to return a Result, however you can return a Result if you want to.

Signature:

class Result<T, E> {
  match<A>(
    okCallback: (value: T) =>  A,
    errorCallback: (error: E) =>  A
  ): A => { ... }
}

match is like chaining map and mapErr, with the distinction that with match both functions must have the same return type.

Example:

const result = computationThatMightFail()

const successCallback = (someNumber: number) => {
  console.log('> number is: ', someNumber)
}

const failureCallback = (someFailureValue: string) => {
  console.log('> boooooo')
}

// method chaining api
// note that you DONT have to append mapErr
// after map which means that you are not required to do
// error handling
result.map(successCallback).mapErr(failureCallback)

// match api
// works exactly the same as above,
// except, now you HAVE to do error handling :)
myval.match(successCallback, failureCallback)

⬆️ Back to top


Result.asyncMap (method)

Similar to map except for two things:

  • the mapping function must return a Promise
  • asyncMap returns a ResultAsync

You can then chain the result of asyncMap using the ResultAsync apis (like map, mapErr, andThen, etc.)

Signature:

class Result<T, E> {
  asyncMap<U>(
    callback: (value: T) => Promise<U>
  ): ResultAsync<U, E> { ... }
}

Example:

import { parseHeaders } 'imaginary-http-parser'
// imagine that parseHeaders has the following signature:
// parseHeaders(raw: string): Result
   

const asyncRes = parseHeaders(rawHeader)
  .map(headerKvMap => headerKvMap.Authorization)
  .asyncMap(findUserInDatabase)

Note that in the above example if parseHeaders returns an Err then .map and .asyncMap will not be invoked, and asyncRes variable will resolve to an Err when turned into a Result using await or .then().

⬆️ Back to top


Result.fromThrowable (static class method)

Although Result is not an actual JS class, the way that fromThrowable has been implemented requires that you call fromThrowable as though it were a static method on Result. See examples below.

The JavaScript community has agreed on the convention of throwing exceptions. As such, when interfacing with third party libraries it's imperative that you wrap third-party code in try / catch blocks.

This function will create a new function that returns an Err when the original function throws.

It is not possible to know the types of the errors thrown in the original function, therefore it is recommended to use the second argument errorFn to map what is thrown to a known type.

Example:

import { Result } from 'neverthrow'

type ParseError = { message: string }
const toParseError = (): ParseError => ({ message: "Parse Error" })

const safeJsonParse = Result.fromThrowable(JSON.parse, toParseError)

// the function can now be used safely, if the function throws, the result will be an Err
const res = safeJsonParse("{");

⬆️ Back to top


Asynchronous API (ResultAsync)

okAsync

Constructs an Ok variant of ResultAsync

Signature:

okAsync<T, E>(value: T): ResultAsync<T, E>

Example:

import { okAsync } from 'neverthrow'

const myResultAsync = okAsync({ myData: 'test' }) // instance of `ResultAsync`

const myResult = await myResultAsync // instance of `Ok`

myResult.isOk() // true
myResult.isErr() // false

⬆️ Back to top


errAsync

Constructs an Err variant of ResultAsync

Signature:

errAsync<T, E>(error: E): ResultAsync<T, E>

Example:

import { errAsync } from 'neverthrow'

const myResultAsync = errAsync('Oh nooo') // instance of `ResultAsync`

const myResult = await myResultAsync // instance of `Err`

myResult.isOk() // false
myResult.isErr() // true

⬆️ Back to top


ResultAsync.fromPromise (static class method)

Transforms a Promise that may throw into a ResultAsync .

The second argument handles the rejection case of the promise and maps the error from unknown into some type E.

Signature:

// fromPromise is a static class method
// also available as a standalone function
// import { fromPromise } from 'neverthrow'
ResultAsync.fromPromise<T, E>(
  promise: Promise<T>,
  errorHandler: (unknownError: unknown) => E)
): ResultAsync<T, E> { ... }

Example:

import { ResultAsync } from 'neverthrow'
import { insertIntoDb } from 'imaginary-database'
// insertIntoDb(user: User): Promise
   

const res = ResultAsync.fromPromise(insertIntoDb(myUser), () => new Error('Database error'))
// `res` has a type of ResultAsync
   

⬆️ Back to top


ResultAsync.fromSafePromise (static class method)

Same as ResultAsync.fromPromise except that it does not handle the rejection of the promise. Ensure you know what you're doing, otherwise a thrown exception within this promise will cause ResultAsync to reject, instead of resolve to a Result.

Signature:

// fromPromise is a static class method
// also available as a standalone function
// import { fromPromise } from 'neverthrow'
ResultAsync.fromSafePromise<T, E>(
  promise: Promise<T>
): ResultAsync<T, E> { ... }

Example:

import { RouteError } from 'routes/error'

// simulate slow routes in an http server that works in a Result / ResultAsync context
// Adopted from https://github.com/parlez-vous/server/blob/2496bacf55a2acbebc30631b5562f34272794d76/src/routes/common/signup.ts
export const slowDown = <T>(ms: number) => (value: T) =>
  ResultAsync.fromSafePromise<T, RouteError>(
    new Promise((resolve) => {
      setTimeout(() => {
        resolve(value)
      }, ms)
    })
  )

export const signupHandler = route<User>((req, sessionManager) =>
  decode(userSignupDecoder, req.body, 'Invalid request body').map((parsed) => {
    return createUser(parsed)
      .andThen(slowDown(3000)) // slowdown by 3 seconds
      .andThen(sessionManager.createSession)
      .map(({ sessionToken, admin }) => AppData.init(admin, sessionToken))
  })
)

⬆️ Back to top


ResultAsync.map (method)

Maps a ResultAsync to ResultAsync by applying a function to a contained Ok value, leaving an Err value untouched.

The applied function can be synchronous or asynchronous (returning a Promise) with no impact to the return type.

This function can be used to compose the results of two functions.

Signature:

class ResultAsync<T, E> {
  map<U>(
    callback: (value: T) => U | Promise<U>
  ): ResultAsync<U, E> { ... }
}

Example:

) => users.map(user => user.name)) // namesInCanada is of type ResultAsync , Error> // We can extract the Result using .then() or await namesInCanada.then((namesResult: Result , Error>) => { if(namesResult.isErr()){ console.log("Couldn't get the users from the database", namesResult.error) } else{ console.log("Users in Canada are named: " + namesResult.value.join(',')) } }) ">
const { findUsersIn } from 'imaginary-database'
// ^ assume findUsersIn has the following signature:
// findUsersIn(country: string): ResultAsync
        
         
          , Error>
         
        

const usersInCanada = findUsersIn("Canada")

// Let's assume we only need their names
const namesInCanada = usersInCanada.map((users: Array<User>) => users.map(user => user.name))
// namesInCanada is of type ResultAsync
        
         
          , Error>
         
        

// We can extract the Result using .then() or await
namesInCanada.then((namesResult: Result<Array<string>, Error>) => {
  if(namesResult.isErr()){
    console.log("Couldn't get the users from the database", namesResult.error)
  }
  else{
    console.log("Users in Canada are named: " + namesResult.value.join(','))
  }
})

⬆️ Back to top


ResultAsync.mapErr (method)

Maps a ResultAsync to ResultAsync by applying a function to a contained Err value, leaving an Ok value untouched.

The applied function can be synchronous or asynchronous (returning a Promise ) with no impact to the return type.

This function can be used to pass through a successful result while handling an error.

Signature:

class ResultAsync<T, E> {
  mapErr<F>(
    callback: (error: E) => F | Promise<F>
  ): ResultAsync<T, F> { ... }
}

Example:

{ // The only error we want to pass to the user is "Unknown country" if(error.message === "Unknown country"){ return error.message } // All other errors will be labelled as a system error return "System error, please contact an administrator." }) // usersInCanada is of type ResultAsync , string> usersInCanada.then((usersResult: Result , string>) => { if(usersResult.isErr()){ res.status(400).json({ error: usersResult.error }) } else{ res.status(200).json({ users: usersResult.value }) } }) ">
const { findUsersIn } from 'imaginary-database'
// ^ assume findUsersIn has the following signature:
// findUsersIn(country: string): ResultAsync
        
         
          , Error>
         
        

// Let's say we need to low-level errors from findUsersIn to be more readable
const usersInCanada = findUsersIn("Canada").mapErr((error: Error) => {
  // The only error we want to pass to the user is "Unknown country"
  if(error.message === "Unknown country"){
    return error.message
  }
  // All other errors will be labelled as a system error
  return "System error, please contact an administrator."
})

// usersInCanada is of type ResultAsync
        
         
          , string>
         
        

usersInCanada.then((usersResult: Result<Array<User>, string>) => {
  if(usersResult.isErr()){
    res.status(400).json({
      error: usersResult.error
    })
  }
  else{
    res.status(200).json({
      users: usersResult.value
    })
  }
})

⬆️ Back to top


ResultAsync.unwrapOr (method)

Unwrap the Ok value, or return the default if there is an Err.
Works just like Result.unwrapOr but returns a Promise instead of T.

Signature:

class ResultAsync<T, E> {
  unwrapOr<T>(value: T): Promise<T> { ... }
}

Example:

const unwrapped: number = await errAsync(0).unwrapOr(10)
// unwrapped = 10

⬆️ Back to top


ResultAsync.andThen (method)

Same idea as map above. Except the applied function must return a Result or ResultAsync.

ResultAsync.andThen always returns a ResultAsync no matter the return type of the applied function.

This is useful for when you need to do a subsequent computation using the inner T value, but that computation might fail.

andThen is really useful as a tool to flatten a ResultAsync , E1> into a ResultAsync (see example below).

Signature:

// Note that the latest version (v4.1.0-beta) lets you return distinct errors as well.
// If the error types (E and F) are the same (like `string | string`)
// then they will be merged into one type (`string`)

class ResultAsync<T, E> {
  andThen<U, F>(
    callback: (value: T) => Result<U, F> | ResultAsync<U, F>
  ): ResultAsync<U, E | F> { ... }
}

Example

const { validateUser } from 'imaginary-validator'
const { insertUser } from 'imaginary-database'
const { sendNotification } from 'imaginary-service'

// ^ assume validateUser, insertUser and sendNotification have the following signatures:
// validateUser(user: User): Result
    
// insertUser(user): ResultAsync
    
// sendNotification(user): ResultAsync
    

const resAsync = validateUser(user)
               .andThen(insertUser)
               .andThen(sendNotification)

// resAsync is a ResultAsync
    

resAsync.then((res: Result<void, Error>) => {
  if(res.isErr()){
    console.log("Oops, at least one step failed", res.error)
  }
  else{
    console.log("User has been validated, inserted and notified successfully.")
  }
})

⬆️ Back to top


ResultAsync.orElse (method)

Takes an Err value and maps it to a ResultAsync . This is useful for error recovery.

Signature:

class ResultAsync<T, E> {
  orElse<A>(
    callback: (error: E) => Result<T, A> | ResultAsync<T, A>
  ): ResultAsync<T, A> { ... }
}

⬆️ Back to top


ResultAsync.match (method)

Given 2 functions (one for the Ok variant and one for the Err variant) execute the function that matches the ResultAsync variant.

The difference with Result.match is that it always returns a Promise because of the asynchronous nature of the ResultAsync.

Signature:

class ResultAsync<T, E> {
  match<A>(
    okCallback: (value: T) =>  A,
    errorCallback: (error: E) =>  A
  ): Promise<A> => { ... }
}

Example:

const { validateUser } from 'imaginary-validator'
const { insertUser } from 'imaginary-database'

// ^ assume validateUser and insertUser have the following signatures:
// validateUser(user: User): Result
    
// insertUser(user): ResultAsync
    

// Handle both cases at the end of the chain using match
const resultMessage = await validateUser(user)
        .andThen(insertUser)
        .match(
            (user: User) => `User ${user.name} has been successfully created`,
            (error: Error) =>  `User could not be created because ${error.message}`
        )

// resultMessage is a string

⬆️ Back to top


Utilities

combine

Combine lists of Results or lists of ResultAsyncs.

If you're familiar with Promise.all, the combine function works conceptually the same.

combine works on both heterogeneous and homogeneous lists. This means that you can have lists that contain different kinds of Results and still be able to combine them. Note that you cannot combine lists that contain both Results and ResultAsyncs.

The combine function takes a list of results and returns a single result. If all the results in the list are Ok, then the return value will be a Ok containing a list of all the individual Ok values.

If just one of the results in the list is an Err then the combine function returns that Err value (it short circuits and returns the first Err that it finds).

Formally speaking:

// homogeneous lists
function combine<T, E>(resultList: Result<T, E>[]): Result<T[], E>

// heterogeneous lists
function combine<T1, T2, E1, E2>(resultList: [ Result<T1, E1>, Result<T2, E2> ]): Result<[ T1, T2 ], E1 | E2>
function combine<T1, T2, T3, E1, E2, E3> => Result<[ T1, T2, T3 ], E1 | E2 | E3>
function combine<T1, T2, T3, T4, E1, E2, E3, E4> => Result<[ T1, T2, T3, T4 ], E1 | E2 | E3 | E4>
// ... etc etc ad infinitum

Additionally, this same function also works for ResultAsync. And thanks to typescript function overloading, the types can be distinguished.

function combine<T, E>(asyncResultList: ResultAsync<T, E>[]): ResultAsync<T[], E>

⬆️ Back to top


combineWithAllErrors

Like combine but without short-circuiting. Instead of just the first error value, you get a list of all error values of the input result list.

If only some results fail, the new combined error list will only contain the error value of the failed results, meaning that there is no guarantee of the length of the new error list.

Like combine, it works for both Result and ResultAsync.

Function signature:

// homogeneous lists
function combineWithAllErrors<T, E>(resultList: Result<T, E>[]): Result<T[], E[]>

// heterogeneous lists
function combineWithAllErrors<T1, T2, E1, E2>(resultList: [ Result<T1, E1>, Result<T2, E2> ]): Result<[ T1, T2 ], (E1 | E2)[]>
function combineWithAllErrors<T1, T2, T3, E1, E2, E3> => Result<[ T1, T2, T3 ], (E1 | E2 | E3)[]>
function combineWithAllErrors<T1, T2, T3, T4, E1, E2, E3, E4> => Result<[ T1, T2, T3, T4 ], (E1 | E2 | E3 | E4)[]>
// ... etc etc ad infinitum

Example usage:

const resultList: Result<number, string>[] = [
  ok(123),
  err('boooom!'),
  ok(456),
  err('ahhhhh!'),
]

const result = combineWithAllErrors(resultList)

// result is Err(['boooom!', 'ahhhhh!'])

⬆️ Back to top


fromThrowable

Top level export of Result.fromThrowable.

Please find documentation at Result.fromThrowable

⬆️ Back to top


fromPromise

Top level export of ResultAsync.fromPromise.

Please find documentation at ResultAsync.fromPromise

⬆️ Back to top


fromSafePromise

Top level export of ResultAsync.fromSafePromise.

Please find documentation at ResultAsync.fromSafePromise

⬆️ Back to top

Testing

Result instances have two unsafe methods, aptly called _unsafeUnwrap and _unsafeUnwrapErr which should only be used in a test environment.

_unsafeUnwrap takes a Result and returns a T when the result is an Ok, otherwise it throws a custom object.

_unsafeUnwrapErr takes a Result and returns a E when the result is an Err, otherwise it throws a custom object.

That way you can do something like:

expect(myResult._unsafeUnwrap()).toBe(someExpectation)

However, do note that Result instances are comparable. So you don't necessarily need to unwrap them in order to assert expectations in your tests. So you could also do something like this:

import { ok } from 'neverthrow'

// ...

expect(callSomeFunctionThatReturnsAResult("with", "some", "args")).toEqual(ok(someExpectation));

By default, the thrown value does not contain a stack trace. This is because stack trace generation makes error messages in Jest harder to understand. If you want stack traces to be generated, call _unsafeUnwrap and / or _unsafeUnwrapErr with a config object:

_unsafeUnwrapErr({
  withStackTrace: true,
})

// ^ Now the error object will have a `.stack` property containing the current stack

If you find this package useful, please consider sponsoring me or simply buying me a coffee!


A note on the Package Name

Although the package is called neverthrow, please don't take this literally. I am simply encouraging the developer to think a bit more about the ergonomics and usage of whatever software they are writing.

Throwing and catching is very similar to using goto statements - in other words; it makes reasoning about your programs harder. Secondly, by using throw you make the assumption that the caller of your function is implementing catch. This is a known source of errors. Example: One dev throws and another dev uses the function without prior knowledge that the function will throw. Thus, and edge case has been left unhandled and now you have unhappy users, bosses, cats, etc.

With all that said, there are definitely good use cases for throwing in your program. But much less than you might think.

Comments
  • Problem with combine() and Mocks

    Problem with combine() and Mocks

    I just spent the last few days chasing my tail on this one. I was using several mocking libraries, notably ts-mockito and testdouble, and got the same behavior. If I created a mock object, wrapped it with okAsync(), and tossed that into combine(), I would get nothing in the return- the mock object was being dropped. Here's an example test in jest that would fail:

    import td from "testdouble";
    
    interface ITestInterface {
        getName(): string;
        setName(name: string): void;
        getAsyncResult(): ResultAsync<ITestInterface, Error>;
    }
    
    describe("Debugging and basic info tests", () => {
    test("combine works with TestDouble mocks of interfaces", async () => {
        // Arrange
        const mock = td.object<ITestInterface>();
    
        // Act
        const result = await combine([okAsync(mock)]);
    
        // Assert
        expect(result).toBeDefined();
        expect(result.isErr()).toBeFalsy();
        const unwrappedResult = result._unsafeUnwrap();
        expect(unwrappedResult.length).toBe(1);
        expect(unwrappedResult[0]).toBe(mock);
      });
    });
    

    If you run this, you'd find that the last two expect calls would fail, because combine would return an empty array! It would only do this with mocks, not with normal objects. So I dug into it, and I found a solution in the combine() code. You are using array.concat(nonArray) in the reduce() of combineResults(). I rolled my own combine and it works even with mocks. I added typing support for heterogenous lists as well, but it looks like you're working on a slicker solution in another issue. Here's my "fixed" combine:

    export class ResultUtils {
        static combine<T, T2, T3, T4, E, E2, E3, E4>(asyncResultList: [ResultAsync<T, E>, ResultAsync<T2, E2>, ResultAsync<T3, E3>, ResultAsync<T4, E4>]): ResultAsync<[T, T2, T3, T4], E | E2 | E3 | E4>;
        static combine<T, T2, T3, E, E2, E3>(asyncResultList: [ResultAsync<T, E>, ResultAsync<T2, E2>, ResultAsync<T3, E3>]): ResultAsync<[T, T2, T3], E | E2 | E3>;
        static combine<T, T2, E, E2>(asyncResultList: [ResultAsync<T, E>, ResultAsync<T2, E2>]): ResultAsync<[T, T2], E | E2>;
        static combine<T, E>(asyncResultList: ResultAsync<T, E>[]): ResultAsync<T[],E> {
            return ResultAsync.fromPromise(Promise.all(asyncResultList), 
            (e) => {return e as E})
            .andThen(ResultUtils.combineResultList);
        }
        
        static combineResultList<T, E>(resultList: Result<T, E>[]): Result<T[], E> {
            return resultList.reduce((acc: Result<T[], E>, result) => {
                return acc.isOk()
                    ? result.isErr()
                        ? err(result.error)
                        : acc.map((values) => {
                            values.push(result.value);
                            return values;
                        })
                    : acc;
            }, ok([]));
        };
    }
    
    bug help wanted question bounty 
    opened by HyperCharlie 21
  • Add eslint `must-use` flag.

    Add eslint `must-use` flag.

    While reading through the docs of Rust's Result type, I learned that they annotate Result with a compiler attribute that enforces a user to "do something" with the result.

    It might be nice to introduce an eslint rule that does this for neverthrow.

    It would prevent situations like this:

    const doSomethingThatMayFail = async () => {
      await changeUserPassword() // Returns a `Result<null, DbError>`
    }
    

    Note that we haven't checked to see if changeUserPassword actually succeeded.


    Edit:

    Learning Resources:

    • https://dev.to/spukas/how-to-write-your-first-eslint-plugin-145
    • https://dev.to/alexgomesdev/writing-custom-typescript-eslint-rules-how-i-learned-to-love-the-ast-15pn
    help wanted bounty 
    opened by supermacro 20
  • Why can't I return different error types in a

    Why can't I return different error types in a "andThen" chain?

    Hi,

    Thank you for publishing this!

    I wanted to get started right away but ran into a seemingly trivial problem I am too much of a noob to solve. Any help would be appreciated!

    The compiler complains when I want to compose multiple results with the .andThen() method

    type E1 = 'invalid'
    type E2 = 'also invalid'
    type E3 = 'invalid again'
    type Errors = E1 | E2 | E3
    
    const f1 = (sth: unknown) => Result<string, E1>
    const f2 = (input: string) => Result<string, E2>
    const f3 = (thing: string) => Result<string, E3>
    
    const result: Result<string, Errors> = f1('test')
                             .andThen(f2)
                             .andThen(f3)
    

    The compiler complains that that the result of f1 is not assignable to the result of f2, specifically Result<string, E1> is not assignable to Result<string, E2>.

    I thought that the mismatch of results and function parameters wouldn’t hurt, because .andThen() doesn’t even call the next function if the result is of type Err. Any idea how I could help with the type inference?

    opened by kolandar 20
  • Heterogeneous `combine` typing

    Heterogeneous `combine` typing

    Hi. With my understanding, only typing stops combine currently from working with heterogeneous Results, implementation looks ready for them. I just prepared types for combine that works for Result of many types, at least in my tests. Could it be useful for the library?

    It will only works for TS 4.0+.

    
    declare function combine<T extends Result<unknown, unknown>[]>(
        resultList: [...T]
      ): CombineResult<T, T>;
    
    
    type CombineResult<T extends unknown[], U extends unknown[]> = T extends [
      infer Element,
      ...(infer Rest)
    ]
      ? Element extends Err<unknown, infer ErrH>
        ? Element
        : CombineResult<Rest, U>
      : Ok<
          { [Index in keyof U]: U[Index] extends Ok<infer OkElement, unknown> ? OkElement : never },
          unknown
        >;
    
    opened by wiktor-obrebski 19
  • Unbounded lists of results without a type annotation don't have enough type information

    Unbounded lists of results without a type annotation don't have enough type information

    This is next proposition to solve problem with #224.

    I feel I learned a lot about TypeScript in last week ;) Today I tried again to provide proper types for Heterogeneous combine function.

    First, I would like to provide my new proposition, implementation of combine types, based on our findings in #224 and my experimetns.

    import { Result, Ok, Err } from 'neverthrow';
    
    declare function combine<T extends readonly Result<unknown, unknown>[]>(
      results: [...T]
    ): Result<ExtractOkTypes<T>, ExtractErrTypes<T>[number]>;
    
    // Given a list of Results, this extracts all the different `T` types from that list
    type ExtractOkTypes<T extends readonly Result<unknown, unknown>[]> = {
      [Key in keyof T]: T[Key] extends Result<unknown, unknown>
        ? ExtractOkFromUnion<T[Key]>
        : never;
    };
    
    // Given a list of Results, this extracts all the different `E` types from that list
    type ExtractErrTypes<T extends readonly Result<unknown, unknown>[]> = {
      [Key in keyof T]: T[Key] extends Result<unknown, unknown>
        ? ExtractErrFromUnion<T[Key]>
        : never;
    };
    
    // need to be separated generic type to run it for every element of union T separately
    type ExtractOkFromUnion<T extends Result<unknown, unknown>> = T extends Ok<
      infer V,
      unknown
    > // filter out "unknown" values
      ? V extends {}
        ? V
        : never
      : never;
    
    // need to be separated generic type to run it for every element of union T separately
    type ExtractErrFromUnion<T extends Result<unknown, unknown>> = T extends Err<
      unknown,
      infer E
    > // filter out "unknown" values
      ? E extends {}
        ? E
        : never
      : never;
    
    
    help wanted bounty hacktoberfest 
    opened by wiktor-obrebski 13
  • Any best practices for testing with Jest?

    Any best practices for testing with Jest?

    I'm using Jest as a test framework. My naive attempts at writing tests for functions that return a Result typically look like:

    expect(callSomeFunction("with", "some", "args")).isOk()).toEqual(true);
    expect(callSomeFunction("with", "some", "args"))._unsafeUnwrap()).toEqual(someExpectation);
    

    This is a bit clumsy because:

    • I need to double all test calls just for the isOk() and _unsafeUnwrap() parts. Especially if the callSomeFunction("with", "some", "args") expression is long, this causes quite some overhead.
    • When the function fails, the runner only outputs that isOk() wasn't true. Any helpful information in the error object remains hidden. I frequently copy paste the expression into a third line for an ad-hoc console.log just to get some information, which of course feels wrong ;).
    • Using _unsafeUnwrap and _unsafeUnwrapErr basically everywhere looks a bit ugly and feels like an anti-pattern.

    I'm be no means a Jest expert and still have to check if it offers something that could help here.

    I just wanted to bring up the topic, perhaps there are already some better approaches?

    opened by bluenote10 13
  • Question: How to recover from an error result to an ok result dynamically?

    Question: How to recover from an error result to an ok result dynamically?

    I see that I can map an ok value to any result I want by using .andThen. I can use unwrapOr to replace every error by given value.

    But what about mapping an error value to an ok value dymically? It's sound quite useful that we want to recover after error and e.g. provide a default value for one type of error and left error as is for other type.

    e.g.

    const occupationResult: Result<string, NotFoundError | ConnectionError> = await getUserOccupation();
    
    const safeOccupationResult = occupationResult.someErrorMappingFunction(
      (error: NotFoundError | ConnectionError) => {
        if (err instanceof NotFoundError) {
          return ok("User not found");
        } else {
          return error;
        }
      }
    );
    
    
    // comparison to promises:
    // a - rejected promise
    const a = Promise.reject("an error");
    // b - resolved promise
    const b = a.catch(err => 33);
    // c - rejected promise
    const c = a.catch(err => Promise.reject("other error"));
    

    how I could achieve currently the result that my imaginary function someErrorMappingFunction do?

    opened by wiktor-obrebski 12
  • ResultAsync is not a valid async function return type

    ResultAsync is not a valid async function return type

    After a lot of research into error handling in TypeScript, I've pretty much settled on this library as being the best option for our use. However, I am running into a major blocker. My existing codebase is built using async functions and await, but after converting the return type of a method from a Promise to ResultAsync<something, someError>, the compiler complains that

    Type 'typeof ResultAsync' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value.

    I am targeting ES5 in my tsconfig, because this is going to run in a browser, but it doesn't make sense to me that it would not be promise compatible. This seems very basic so I figure I must be missing something here; is there a config option or something I may be overlooking? For the record, I'm using neverthrow 3.1.2.

    As an example, even this basic method throws an error:

    public async testAsync(): ResultAsync<string, Error> { return okAsync("Hello world!"); }

    opened by HyperCharlie 12
  • API improvement ideas (usable behind @beta flag on npm)

    API improvement ideas (usable behind @beta flag on npm)

    Just writing this here publicly to see if others agree or disagree.

    I think it would be nice to have a utility function that has the following signature:

    Promise<Result<T, E>> => ResultAsync<T, E>
    

    Since sometimes it's easier to write a function using async/await syntax.


    Additionally, I am also thinking it would be nice to have another utility function with the following signature:

    Result<T, E> => ResultAsyncT, E>
    

    I guess the above would be called intoResultAsync or something like that?

    Curious to hear what folks think.

    opened by supermacro 11
  • chain overloading

    chain overloading

    Hey supermacro, thanks so much for this library :) I'm starting to use it in one of my projects and love it so far! Anyway I was just wondering if you considered using function overloading for the chain API? I'm imaging something similar to the pipe API in fp-ts.

    Here is kinda what I'm thinking although this has not been tested:

    export function chain<T1, T2, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Promise<Result<T2, E>>): Promise<Result<T2, E>>;
    export function chain<T1, T2, T3, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Ok<T2, E> | Err<T2, E> | Promise<Result<T2, E>>, r3: (v: T2) => Promise<Result<T3, E>>): Promise<Result<T3, E>>;
    export function chain<T1, T2, T3, T4, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Ok<T2, E> | Err<T2, E> | Promise<Result<T2, E>>, r3: (v: T2) => Ok<T3, E> | Err<T3, E> | Promise<Result<T3, E>>, r4: (v: T3) => Promise<Result<T4, E>>): Promise<Result<T4, E>>;
    export function chain<T1, T2, T3, T4, T5, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Ok<T2, E> | Err<T2, E> | Promise<Result<T2, E>>, r3: (v: T2) => Ok<T3, E> | Err<T3, E> | Promise<Result<T3, E>>, r4: (v: T3) => Ok<T4, E> | Err<T4, E> | Promise<Result<T4, E>>, r5: (v: T4) => Promise<Result<T5, E>>): Promise<Result<T5, E>>;
    export function chain<T1, T2, T3, T4, T5, T6, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Ok<T2, E> | Err<T2, E> | Promise<Result<T2, E>>, r3: (v: T2) => Ok<T3, E> | Err<T3, E> | Promise<Result<T3, E>>, r4: (v: T3) => Ok<T4, E> | Err<T4, E> | Promise<Result<T4, E>>, r5: (v: T4) => Ok<T5, E> | Err<T5, E> | Promise<Result<T5, E>>, r6: (v: T5) => Promise<Result<T6, E>>): Promise<Result<T6, E>>;
    export function chain<T1, T2, T3, T4, T5, T6, T7, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Ok<T2, E> | Err<T2, E> | Promise<Result<T2, E>>, r3: (v: T2) => Ok<T3, E> | Err<T3, E> | Promise<Result<T3, E>>, r4: (v: T3) => Ok<T4, E> | Err<T4, E> | Promise<Result<T4, E>>, r5: (v: T4) => Ok<T5, E> | Err<T5, E> | Promise<Result<T5, E>>, r6: (v: T5) => Ok<T6, E> | Err<T6, E> | Promise<Result<T6, E>>, r7: (v: T6) => Promise<Result<T7, E>>): Promise<Result<T7, E>>;
    export function chain<T1, T2, T3, T4, T5, T6, T7, T8, E>(r1: Promise<Result<T1, E>>, r2: (v: T1) => Ok<T2, E> | Err<T2, E> | Promise<Result<T2, E>>, r3: (v: T2) => Ok<T3, E> | Err<T3, E> | Promise<Result<T3, E>>, r4: (v: T3) => Ok<T4, E> | Err<T4, E> | Promise<Result<T4, E>>, r5: (v: T4) => Ok<T5, E> | Err<T5, E> | Promise<Result<T5, E>>, r6: (v: T5) => Ok<T6, E> | Err<T6, E> | Promise<Result<T6, E>>, r7: (v: T6) => Ok<T7, E> | Err<T7, E> | Promise<Result<T7, E>>, r8: (v: T7) => Promise<Result<T8, E>>): Promise<Result<T8, E>>;
    
    export async function chain(
      start: Promise<Result<any, any>>,
      ...transforms: any
    ): Promise<Result<any, any>> {
      return await (transforms as Array<(v: any) => Promise<Result<any, any>>>).reduce(
        async (current, f) => {
          const mapped = await (await current).asyncMap(f);
          return mapped.andThen((inner) => inner);
        },
        start,
      );
    }
    

    Let me know your thoughts!

    opened by jsmith 11
  • Better integration with async/await

    Better integration with async/await

    Hi there,

    I know that this has come up a few times in a few different ways, but I'm coming at it from a different point of view.

    One of the major benefits of async/await is that it helps to make the code more readable. You get to write more procedural code that is more obvious to read and maintain, instead of complicated call stacks.

    For example, the following are the same:

    return doSomething()
        .then(result => doSomethingElse(result))
        .then(next => next.abc);
    // -----
    const result = await doSomething();
    const next = await doSomethingElse(result);
    return next.abc;
    

    The second of which is much more obvious for someone to follow.

    Neverthrow is awesome, but the fact that it not only doesn't work with async/await but also doesn't work well with Promise makes it awkward to use. If I start to use it in a call stack I end up having to use it throughout the entire call stack, or else using things like _unsafeUnwrap() to escape from it.

    This means that it's more difficult to introduce into a project. I have to start from the outside and work in, because I can't easily convert a layer until all of the callers are already expecting ResultAsync objects to be returned. And this is especially big if you consider infrastructure-level layers. For example, if I were to update a database layer to use neverthrow instead of exceptions then I've got to update the entire application in order to make that work!

    The frustrating thing is that Promise<Result<T, E>> almost works fine. It's just the fact that the various methods on it - map, andThen, etc - don't allow for async methods that cause problems.

    For example, the following would - to me, at least - be a much easier to understand and maintain alternative:

    return doSomething() // function doSomething() : ResultAsync<string, Error>
        .andThen(doSomethingElse) // function doSomethingElse(string) : ResultAsync<foo, Error>
        .map(next => next.abc());
    // -----
    const result = await doSomething(); // async function doSomething() : Promise<Result<string, Error>>
    const next = await result.andThenAsync(doSomethingElse); // async function doSomethingElse(string) : Promise<Result<foo, Error>>
    return await next.mapAsync(n => n.abc());
    

    where in this case the andThenAsync and mapAsync functions take async functions and return Promise<Result<T, E>> instead. And, importantly, it would mean that I can replace any individual part of the call chain with neverthrow without needing to replace all of it.

    opened by sazzer 10
  • Update dependency prettier to v2.8.1

    Update dependency prettier to v2.8.1

    Mend Renovate

    This PR contains the following updates:

    | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | prettier (source) | 2.2.1 -> 2.8.1 | age | adoption | passing | confidence |


    Release Notes

    prettier/prettier

    v2.8.1

    Compare Source

    diff

    Fix SCSS map in arguments (#​9184 by @​agamkrbit)
    // Input
    $display-breakpoints: map-deep-merge(
      (
        "print-only": "only print",
        "screen-only": "only screen",
        "xs-only": "only screen and (max-width: #{map-get($grid-breakpoints, "sm")-1})",
      ),
      $display-breakpoints
    );
    
    // Prettier 2.8.0
    $display-breakpoints: map-deep-merge(
      (
        "print-only": "only print",
        "screen-only": "only screen",
        "xs-only": "only screen and (max-width: #{map-get($grid-breakpoints, " sm
          ")-1})",
      ),
      $display-breakpoints
    );
    
    // Prettier 2.8.1
    $display-breakpoints: map-deep-merge(
      (
        "print-only": "only print",
        "screen-only": "only screen",
        "xs-only": "only screen and (max-width: #{map-get($grid-breakpoints, "sm")-1})",
      ),
      $display-breakpoints
    );
    
    Support auto accessors syntax (#​13919 by @​sosukesuzuki)

    Support for Auto Accessors Syntax landed in TypeScript 4.9.

    (Doesn't work well with babel-ts parser)

    class Foo {
      accessor foo: number = 3;
    }
    

    v2.8.0

    Compare Source

    diff

    🔗 Release Notes

    v2.7.1

    Compare Source

    diff

    Keep useful empty lines in description (#​13013 by @​chimurai)

    v2.7.0

    Compare Source

    """ First line Second Line """ type Person { name: String }

    v2.6.2

    Compare Source

    diff

    Fix LESS/SCSS format error (#​12536 by @​fisker)
    // Input
    .background-gradient(@&#8203;cut) {
        background: linear-gradient(
            to right,
            @&#8203;white 0%,
            @&#8203;white (@&#8203;cut - 0.01%),
            @&#8203;portal-background @&#8203;cut,
            @&#8203;portal-background 100%
        );
    }
    
    // Prettier 2.6.1
    TypeError: Cannot read properties of undefined (reading 'endOffset')
    
    // Prettier 2.6.2
    .background-gradient(@&#8203;cut) {
      background: linear-gradient(
        to right,
        @&#8203;white 0%,
        @&#8203;white (@&#8203;cut - 0.01%),
        @&#8203;portal-background @&#8203;cut,
        @&#8203;portal-background 100%
      );
    }
    
    Update meriyah to fix several bugs (#​12567 by @​fisker, fixes in meriyah by @​3cp)

    Fixes bugs when parsing following valid code:

    foo(await bar());
    
    const regex = /.*/ms;
    
    const element = <p>{/w/.test(s)}</p>;
    
    class A extends B {
      #privateMethod() {
        super.method();
      }
    }
    

    v2.6.1

    Compare Source

    diff

    Ignore loglevel when printing information (#​12477 by @​fisker)

    v2.6.0

    Compare Source

    prettier --loglevel silent --find-config-path index.js

    v2.5.1

    Compare Source

    diff

    Improve formatting for empty tuple types (#​11884 by @​sosukesuzuki)
    // Input
    type Foo =
      Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooo extends []
        ? Foo3
        : Foo4;
    
    // Prettier 2.5.0
    type Foo = Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooo extends [
    
    ]
      ? Foo3
      : Foo4;
    
    // Prettier 2.5.0 (tailingCommma = all)
    // Invalid TypeScript code
    type Foo = Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooo extends [
      ,
    ]
      ? Foo3
      : Foo4;
    
    // Prettier 2.5.1
    type Foo =
      Foooooooooooooooooooooooooooooooooooooooooooooooooooooooooo extends []
        ? Foo3
        : Foo4;
    
    
    Fix compatibility with Jest inline snapshot test (#​11892 by @​fisker)

    A internal change in [email protected] accidentally breaks the Jest inline snapshot test.

    Support Glimmer's named blocks (#​11899 by @​duailibe)

    Prettier already supported this feature, but it converted empty named blocks to self-closing, which is not supported by the Glimmer compiler.

    See: Glimmer's named blocks.

    // Input
    <Component>
      <:named></:named>
    </Component>
    
    // Prettier 2.5.0
    <Component>
      <:named />
    </Component>
    
    // Prettier 2.5.1
    <Component>
      <:named></:named>
    </Component>
    

    v2.5.0

    Compare Source

    diff

    🔗 Release Notes

    v2.4.1

    Compare Source

    diff

    Fix wildcard syntax in @forward (#​11482) (#​11487 by @​niksy)
    // Input
    @&#8203;forward "library" as btn-*;
    
    // Prettier 2.4.0
    @&#8203;forward "library" as btn- *;
    
    // Prettier 2.4.1
    @&#8203;forward "library" as btn-*;
    
    Add new CLI option debug-print-ast (#​11514 by @​sosukesuzuki)

    A new --debug-print-ast CLI flag for debugging.

    v2.4.0

    Compare Source

    diff

    🔗 Release Notes

    v2.3.2

    Compare Source

    diff

    Fix failure on dir with trailing slash (#​11000 by @​fisker)
    $ ls
    1.js  1.unknown
    

    v2.3.1

    Compare Source

    $ prettier . -l 1.js $ prettier ./ -l [error] No supported files were found in the directory: "./".

    v2.3.0

    Compare Source

    diff

    🔗 Release Notes


    Configuration

    📅 Schedule: Branch creation - "every 1 months on the first day of the month" (UTC), Automerge - At any time (no schedule defined).

    🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

    Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

    🔕 Ignore: Close this PR and you won't be reminded about this update again.


    • [ ] If you want to rebase/retry this PR, check this box

    This PR has been generated by Mend Renovate. View repository job log here.

    opened by renovate[bot] 0
  • Update dependency @types/node to v14.18.36

    Update dependency @types/node to v14.18.36

    Mend Renovate

    This PR contains the following updates:

    | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | @types/node (source) | 14.17.4 -> 14.18.36 | age | adoption | passing | confidence |


    Configuration

    📅 Schedule: Branch creation - "every 1 months on the first day of the month" (UTC), Automerge - At any time (no schedule defined).

    🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

    Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

    🔕 Ignore: Close this PR and you won't be reminded about this update again.


    • [ ] If you want to rebase/retry this PR, check this box

    This PR has been generated by Mend Renovate. View repository job log here.

    opened by renovate[bot] 0
  • Update dependency testdouble to v3.16.8

    Update dependency testdouble to v3.16.8

    Mend Renovate

    This PR contains the following updates:

    | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | testdouble | 3.16.3 -> 3.16.8 | age | adoption | passing | confidence |


    Release Notes

    testdouble/testdouble.js

    v3.16.8

    Compare Source

    v3.16.7

    Compare Source

    • Update typings around replace and related methods #​499

    v3.16.6

    Compare Source

    v3.16.5

    Compare Source

    • Improve type definition for stubbing a sequence of promises #​481

    v3.16.4

    Compare Source

    • Update quibble

    Configuration

    📅 Schedule: Branch creation - "every 1 months on the first day of the month" (UTC), Automerge - At any time (no schedule defined).

    🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

    Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

    🔕 Ignore: Close this PR and you won't be reminded about this update again.


    • [ ] If you want to rebase/retry this PR, check this box

    This PR has been generated by Mend Renovate. View repository job log here.

    opened by renovate[bot] 0
  • orElse returns the wrong type when the callback changes the ok type

    orElse returns the wrong type when the callback changes the ok type

    Result is broken. It doesn't track ok type changes.

    import { Result, ok, err } from 'neverthrow';
    
    const a: Result<number, number> = err(1);
    const b: Result<number, number> = a.orElse(() => ok('A'));
    console.log(b);
    
    $ npx tsc
    $ node main.js
    Ok { value: 'A' }
    

    The ok type of b is string, but TypeScript thinks it is number.

    ResultAsync is fine. It rejects ok type changes.

    import { ResultAsync, okAsync, errAsync } from 'neverthrow';
    
    const a: ResultAsync<number, number> = errAsync(1);
    const b: ResultAsync<number, number> = a.orElse(() => okAsync('A'));
    b.then((r) => console.log(r));
    
    $ npx tsc
    main.ts:4:7 - error TS2322: Type 'ResultAsync<number, unknown>' is not assignable to type 'ResultAsync<number, number>'.
      Type 'unknown' is not assignable to type 'number'.
    
    4 const b: ResultAsync<number, number> = a.orElse(() => okAsync('A'));
            ~
    
    main.ts:4:55 - error TS2769: No overload matches this call.
      Overload 1 of 3, '(f: (e: number) => Result<number, unknown>): ResultAsync<number, unknown>', gave the following error.
        Type 'ResultAsync<string, never>' is not assignable to type 'Result<number, unknown>'.
          Type 'ResultAsync<string, never>' is missing the following properties from type 'Err<number, unknown>': error, isOk, isErr, asyncAndThen, and 3 more.
      Overload 2 of 3, '(f: (e: number) => ResultAsync<number, unknown>): ResultAsync<number, unknown>', gave the following error.
        Type 'ResultAsync<string, never>' is not assignable to type 'ResultAsync<number, unknown>'.
          Type 'string' is not assignable to type 'number'.
      Overload 3 of 3, '(f: (e: number) => Result<number, never> | ResultAsync<number, never>): ResultAsync<number, never>', gave the following error.
        Type 'ResultAsync<string, never>' is not assignable to type 'Result<number, never> | ResultAsync<number, never>'.
          Type 'ResultAsync<string, never>' is not assignable to type 'ResultAsync<number, never>'.
            Type 'string' is not assignable to type 'number'.
    
    4 const b: ResultAsync<number, number> = a.orElse(() => okAsync('A'));
                                                            ~~~~~~~~~~~~
    
      node_modules/neverthrow/dist/index.d.ts:19:45
        19     orElse<R extends Result<T, unknown>>(f: (e: E) => R): ResultAsync<T, InferErrTypes<R>>;
                                                       ~~~~~~~~~~~
        The expected type comes from the return type of this signature.
      node_modules/neverthrow/dist/index.d.ts:20:50
        20     orElse<R extends ResultAsync<T, unknown>>(f: (e: E) => R): ResultAsync<T, InferAsyncErrTypes<R>>;
                                                            ~~~~~~~~~~~
        The expected type comes from the return type of this signature.
      node_modules/neverthrow/dist/index.d.ts:21:18
        21     orElse<A>(f: (e: E) => Result<T, A> | ResultAsync<T, A>): ResultAsync<T, A>;
                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        The expected type comes from the return type of this signature.
    
    
    Found 2 errors in the same file, starting at: main.ts:4
    
    opened by lantw44 1
  • Bump minimatch from 3.0.4 to 3.1.2

    Bump minimatch from 3.0.4 to 3.1.2

    Bumps minimatch from 3.0.4 to 3.1.2.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Enormous declaration files that hang everything since 5.1.0

    Enormous declaration files that hang everything since 5.1.0

    I want to re-export the combine functions like this

    import { Result } from 'neverthrow';
    export function combine<T extends Result<unknown, unknown>[]>(resultList: T) {
        return Result.combine(resultList);
    }
    

    It works fine, but the d.ts file becomes so huge that it entirely hangs the IDE and the typescript compiler, so it's impossible to use that declaration file in any other project.

    I don't know why that happens, maybe it's a tsc issue or something else, but it was working fine before version 5.1.0.

    image

    opened by lmcsu 2
Releases(v6.0.0)
  • v6.0.0(Dec 11, 2022)

    The combine* family of functions (both sync and async) broke any codebase that called these functions on arrays of varying lengths when the previous release was shipped:

    https://github.com/supermacro/neverthrow/releases/tag/v5.1.0

    The following fix was implemented to address this issue and ensure that arrays (not just tuples) work with the combine* family of functions.

    https://github.com/supermacro/neverthrow/pull/435

    Why is this shipped as a MAJOR version change?

    Because we are now explicitly depending on TypeScript's variadic tuple types feature, which came out in v4 of TypeScript:

    https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#variadic-tuple-types

    Acknowledgements

    Thank you to @ghost91- for implementing the fix 🙏

    Source code(tar.gz)
    Source code(zip)
  • v5.1.0(Oct 24, 2022)

    Thanks to @incetarik for fixing a long-lasting developer inconvenience! https://github.com/supermacro/neverthrow/issues/226

    Now, all of the "combine" functions can now infer types correctly and treat the list argument as a tuple rather than an unbounded list!

    i.e. Rather than having to do:

    Result.combine([ ok(123), err('whopps!') ] as const)
    

    You can just omit the as const now:

    Result.combine([ ok(123), err('whopps!') ])
    

    And neverthrow can figure out the type of the above tuple on its own!

    Source code(tar.gz)
    Source code(zip)
  • v5.0.1(Oct 9, 2022)

    Thank you to @ccjmne @lmcsu and @msheakoski for this improvement :pray:

    This release ships https://github.com/supermacro/neverthrow/pull/425 - which ensures that the E type for ResultAsync.fromSafePromise is never by default - which makes a lot more sense / is more a "accurate" depiction of runtime behaviour.

    Source code(tar.gz)
    Source code(zip)
  • v5.0.0(Jul 28, 2022)

    Thank you to @tam-carre for implementing this fix!

    Unfortunately there is no way to distinguish at runtime if an empty list is a list of ResultAsyncs or a list of Results.

    Hence https://github.com/supermacro/neverthrow/issues/381 ... which could cause runtime exceptions!

    So we've had to make the unfortunate decision of splitting combine and combineWithAllErrors into a synchronous version (behind the Result namespace) and an asynchronous version (behind the ResultAsync namespace).

    So now we have:

    • Result.combine
    • Result.combineWithAllErrors
    • ResultAsync.combine
    • ResultAsync.combineWithAllErrors

    ... of course, this is a breaking change, and as such neverthrow has now been bumped to version 5.0.0

    Source code(tar.gz)
    Source code(zip)
  • v4.4.2(Jul 10, 2022)

    Thanks to @bhvngt for this submission.

    ResultAsync.fromPromise and ResultAsync.fromSafePromise are now able to accept anything that implements the PromiseLike interface - in other words; anything that is "thenable". This includes objects such as Bluebird Promises and any other non-native promise-like object!

    Source code(tar.gz)
    Source code(zip)
  • v4.3.1(Nov 19, 2021)

    User kieran-osgood submitted a fix for a long standing issue whereby proxies were suddenly disappearing within calls to combine:

    https://github.com/supermacro/neverthrow/issues/228

    This issue is now resolved!

    Additionally, the docs have been updated to reference eslint-plugin-neverthrow.

    Source code(tar.gz)
    Source code(zip)
  • v4.3.0(Oct 18, 2021)

    Many thanks to @sidhu663 for these great improvements to neverthrow.

    Significantly Improved Type Inference On andThen and orElse Methods

    With this release, you should not need to explicitly annotate the return type on the callbacks called into andThen and orElse.

    Example:

    Before

    const slotsResult = ok(123).andThen((value) =>
      value === '777'
        ? ok('Lucky Sevens')
        : err('Try Again')
    )
    

    Before, slotsResult would be of type Result<unknown, unknown>.

    The "solution" was to explicitly annotate your callback function as returning Result<string, string>

    After

    This exact piece of code above now types slotsResult as Result<string, string>!

    If you're interested in how this works, check out the PR that implements these improvements here: https://github.com/supermacro/neverthrow/pull/349

    Additional notes

    • This same improvement applies to .orElse
    • These improvements are for both Result and ResultAsync

    More Flexible unwrapOr Method

    The type signature of .unwrapOr has been "widened" to allow for returning something other than a type T as defined by the Result / ResultAsync you are calling unwrapOr on.

    This change was implemented in https://github.com/supermacro/neverthrow/pull/350.

    Example:

    const result = err<number, string>('boom').unwrapOr(false)
    

    The above did not work prior to v4.3.0 because boolean (the false value) is not of type number.

    But now you are not constrained by this! As of v4.3.0 you now have the ability to specify a value of any type within unwrapOr.

    In the above example, result is now of type number | boolean.

    Source code(tar.gz)
    Source code(zip)
  • v4.2.2(Jul 1, 2021)

    Fixes #300

    This PR updates the type definition of Result.fromThrowable in order to allow accepting functions that take 1 - n arguments.

    Thanks to @universalhandle for the contribution 👏

    Source code(tar.gz)
    Source code(zip)
  • v4.2.1(Mar 20, 2021)

    This release adds in-line docs for the Result variants. Now, when using a LSP-supported editor, you'll have docs as you hover a Result method.

    Thanks to @Liam-Tait for the contribution!

    Source code(tar.gz)
    Source code(zip)
  • v4.2.0(Mar 16, 2021)

    neverthrow now exposes a combineWithAllErrors function that is the "lazy" (for lack of a better word) equivalent of combine.

    This means that rather than short-circuiting upon finding the first Err value, combineWithAllErrors will continue consuming the Result / ResultAsync list until the end and return a list containing all the Err values that it found.

    Thanks to @tobloef for the contribution 🚀

    Source code(tar.gz)
    Source code(zip)
  • v4.1.1(Feb 25, 2021)

    This release patches a bug where calling combine on a Result<T, E>[] or ResultAsync<T, E>[] and the T values were lists, then those lists would get concatenated into a single output list:

    Example:

    import { combine, ok } from 'neverthrow'
    
    combine([
      ok([ 1, 2, 3 ]),
      ok([ 'hello', 'world ])
    ])
    
    // before v4.1.1, the above would incorrectly return [ 1, 2, 3, 'hello', 'world' ]
    // 
    // actual output should be [ [ 1,2,3 ], [ 'hello', 'world' ] ] 
    

    The fix of the bug can be found here:

    https://github.com/supermacro/neverthrow/commit/a5e66a88f8257f3afb74787e65bc04a1bf1a3812

    I decided to continue using Array.prototype.concat in spite of this subtlety because it does not mutate references (unlike Array.prototype.push).

    Source code(tar.gz)
    Source code(zip)
  • v4.1.0(Feb 15, 2021)

    This release:

    • Releases https://github.com/supermacro/neverthrow/releases/tag/v4.1.0-beta.0 out of beta

      • This functionality is now part of the @default npm tag, meaning you can just run npm install neverthrow to get this functionality
    • Re-exporting fromThrowable, fromPromise and fromSafePromise (PR here)

      • Thanks to @mariosant for the contribution
    Source code(tar.gz)
    Source code(zip)
  • v4.1.0-beta.0(Jan 26, 2021)

    I finally caved and have gone ahead and "loosened" the restriction that all andThen variations (Result.andThen, Result.asyncAndThen, and ResultAsync.andThen) had before. The restriction prevented the callback inside all andThen variations from returning a distinct / different error type.

    See https://github.com/supermacro/neverthrow/issues/30 for more context on the "problem" that this addresses.

    This release is downloadable behind a beta tag on npm.

    > npm install neverthrow@beta
    

    Docs have been updated to reflect this change.

    Here's an example diff.

    -   andThen<U>(f: (t: T) => Result<U, E>): Result<U, E> {
    +   andThen<U, F>(f: (t: T) => Result<U, F>): Result<U, E | F> {
    

    And here is the full diff:

    https://github.com/supermacro/neverthrow/commit/8f87ef050279cf17e81b4f747abace84540472f5

    Source code(tar.gz)
    Source code(zip)
  • v4.0.1(Jan 17, 2021)

    The combine overload for ResultAsync lists was incorrect. Previously the overload stated that the return type of a combine invocation with a list of ResultAsyncs was a Result.

    This patch fixes this error and now combine's type on ResultAsyncs correctly specifies that the return value will be a ResultAsync.

    Source code(tar.gz)
    Source code(zip)
  • v4.0.0(Jan 15, 2021)

    Breaking Change: This release changes the API of the original ResultAsync.fromPromise method to require a error handler callback.

    In addition, there is now a new static class method called ResultAsync.fromSafePromise that does not take a error handler callback under the assumption that you are calling this method with a promise that does not ever throw.

    Source code(tar.gz)
    Source code(zip)
  • v3.2.0(Jan 13, 2021)

    Combine v2

    The combine utility function now lets you combine lists containing different kinds of results! The recommended way that you use combine when calling it with a heterogeneous list is to either declare a tuple type that describes each element in the list, or declare the list as const.

    New orElse method

    The orElse method (implemented on both Result and ResultAsync) allows for error-recovery where you want to potentially convert an E into T.

    Source code(tar.gz)
    Source code(zip)
  • 3.1.4(Dec 28, 2020)

    This release swaps out the usage of a native Error with a custom JS Object for the _unsafeUnwrap and _unsafeUnwrapErr methods. This means that stack traces will not be generated by default.

    The aforementioned methods will not generate a stack trace because Jest's error messages become less helpful.

    If for some reason you want to log stack traces (because you're using these methods in a non-test environment ... which would be considered .... unsafe) for thrown exceptions, you can call the above methods with the following config object as an argument:

    _unsafeUnwrapErr({
      withStackTrace: true,
    })
    
    Source code(tar.gz)
    Source code(zip)
  • v3.1.3(Dec 22, 2020)

    https://github.com/supermacro/neverthrow/pull/214 changes the match method from being implemented as a instance property to being implemented as a method on the prototype chain.

    Therefore all instances of Result share the same reference in their match method. This allows testing libraries like Jest to make appropriate object comparisons / diffs.

    Thanks to @bluenote10 for the submission!

    Source code(tar.gz)
    Source code(zip)
  • v3.1.2(Dec 9, 2020)

    Thanks to @Liam-Tait for the new fromThrowable function.

    The fromThrowable function is a higher-order-function that wraps functions that throw (such as JSON.parse).

    See docs on fromThrowable for more details.

    Source code(tar.gz)
    Source code(zip)
  • v3.0.0(Nov 20, 2020)

    New Features

    This release brings two previous beta features into general availability:

    • ResultAsync is now PromiseLike
      • docs / intro: https://github.com/supermacro/neverthrow/releases/tag/v2.8.0-beta.1
    • combine API no longer behind a beta flag
      • You can read more about the combine utility function in the docs

    Other Changes

    • Documentation has been cleaned up
      • A lot of what was in the README has been moved over to the wiki

    Breaking Changes

    • The entire chain module has been removed. The recommended way to work with async results has been to use ResultAsync.
    Source code(tar.gz)
    Source code(zip)
  • v2.8.0-beta.1(Oct 2, 2020)

    ResultAsync now implements the PromiseLike interface to allow deep integration with promise api's (gone are the days of awkward workarounds). You can now do the following without a compilation error:

    const result = await Promise.all([ resultAsync1, resultAsync2 ]).then(makeAResultAsync)
    

    Thanks to @paduc for the contribution!

    Because of the new combine api that was released yesterday, this feature will also get released behind a @beta flag.

    > npm install neverthrow@beta
    
    Source code(tar.gz)
    Source code(zip)
  • v2.8.0-beta.0(Oct 1, 2020)

    This release introduces a small, but very useful, utility function called combine.

    If you're familiar with Promise.all, the combine function works conceptually the same.

    The combine function takes a list of results and return a single result. If all the results in the list are Ok, then the return value will be a Ok containing a list of all the individual Ok values.

    If just one of the results in the list is an Err then the combine function returns that Err value (it short circuits and returns the first Err that it finds).

    Formally speaking:

    function combine<T, E>(resultList: Result<T, E>[]): Result<T[], E>
    

    Additionally, this same function also works for ResultAsync. And thanks to typescript function overloading, the types can be distinguished.

    function combine<T, E>(asyncResultList: ResultAsync<T, E>[]): ResultAsync<T[], E>
    

    Note that there are no docs yet since this feature is hidden behind a @beta flag on npm.

    To try this new api out, install the @beta tag of neverthrow

    npm install neverthrow@beta
    

    Additional changes

    • Remove references to chain api in README in preparation for removal of the code in a breaking release.
      • Still not sure on when I'll do this. I want to continue dog fooding ResultAsync before completely committing to removing the chain api. Feedback / thoughts welcome view github issues!
    Source code(tar.gz)
    Source code(zip)
  • v2.7.1(Jul 24, 2020)

  • v2.7.0(Jul 2, 2020)

    The previous release introduced a super handy method for unwrapping Result<T, E> into a T in a typesafe way.

    This release introduces the same unwrapOr method, but for ResultAsync<T, E>!

    ResultAsync<T, E>.unwrapOr(defaultValue: T) => Promise<T>
    

    Thanks to @paduc for opening up the PR!

    Source code(tar.gz)
    Source code(zip)
  • v2.6.0(Jul 1, 2020)

    This PR introduces a new method for synchronous Results:

    ✨ ✨ unwrapOr ✨ ✨

    Type signature:

    Result<T, E>.unwrapOr(default: T) => T
    

    The method takes a Result and returns the inner T when you have an Ok, or it returns the default value when you have an Err.

    Thanks to @pawndev for the contribution! 🚀

    Source code(tar.gz)
    Source code(zip)
  • v2.5.0-beta(Jun 29, 2020)

    You can now use neverthrow as an ES Module. This is a non-breaking change as you can still continue using neverthrow as a CommonJS module.

    See discussion here: https://github.com/supermacro/neverthrow/issues/58

    To try out ES Modules, install using the beta flag:

    npm install neverthrow@beta
    
    Source code(tar.gz)
    Source code(zip)
  • v2.5.0(Jun 29, 2020)

  • v2.4.0(Jun 16, 2020)

    This release introduces a new API method called asyncAndThen.

    asyncAndThen is the asynchronous version of andThen.

    Rather than accepting a (val: T) => Result<U, E>, asyncAndThen accepts a (val: T) => ResultAsync<U, E>.

    Thank you to @jsmith and @paduc for their help in finding the bug reported in https://github.com/supermacro/neverthrow/issues/53 and then implementing the fix / new API.

    Source code(tar.gz)
    Source code(zip)
  • 2.3.3(May 30, 2020)

    Thanks to @jsmith for submitting a patch which addresses this issue:

    • fromPromise always prints warning (#46)

    This release also includes some minor additions as well:

    • dev dependency upgrades
    • generalizing the logging logic, see this commit
    • log deprecation warning for the chain API
    Source code(tar.gz)
    Source code(zip)
  • 2.3.2(May 27, 2020)

    This release patches, but doesn't necessarily address, https://github.com/supermacro/neverthrow/issues/39.

    Besides that, there are was a bit of "cleanup" around upgrading dev dependencies.

    Source code(tar.gz)
    Source code(zip)
Owner
Gio
It'sa meeee
Gio
Creates a URL slug as you type a page title (like Django slugify())

jQuery Slugify Update Please have a look at Madflow's implementation of jQuery Slugify. It's a more mature and active project. In theory, you should b

Patrick McElhaney 87 Jun 28, 2022
autoNumeric is a standalone library that provides live as-you-type formatting for international numbers and currencies.

What is autoNumeric? autoNumeric is a standalone Javascript library that provides live as-you-type formatting for international numbers and currencies

AutoNumeric 1.7k Dec 16, 2022
Online Golang Struct to TypeScript Interface Converter

Golang Struct to TypeScript Interface Use this tool live! https://stirlingmarketinggroup.github.io/go2ts/ This tool converts Go structs to TypeScript

Stirling Marketing Group 42 Dec 17, 2022
TypeScript with a Borrow Checker. Multi-threaded, Tiny binaries. No GC. Easy to write.

TypeScript with a Borrow Checker. Multi-threaded, Tiny binaries. No GC. Easy to write.

David Alsh 1.4k Dec 19, 2022
Shikimori.ts - JavaScript & TypeScript wrapper for shikimori.one

shikimori.ts shikimori.ts - JavaScript & TypeScript wrapper for shikimori.one Features Full TypeScript support Support all platforms Easy to use Table

null 5 Sep 15, 2022
A simple bot for Lingos exercises automation written entirely in Typescript.

Linger The spiritual successor to lingus Stack used Typescript SWC Yarn Nodejs Puppeteer Dotenv UUID Nodemon Instalation Prerequisites: Nodejs: ^16.13

TlenDev 2 May 9, 2022
An implementation of cellular automata in TypeScript

?? cellular-automata An implementation of cellular automata in TypeScript. ?? Usage First of all, clone the repository: $ git clone [email protected]:aro

Faye's Games 2 Feb 7, 2022
Typescript template for a discord bot using discord.js

Discord-Typescript-Template Typescript template for a discord bot using discord.js. Installation Clone the repository git clone https://github.com/Ami

null 2 Oct 17, 2022
ThinkMore Forum frontend build with Next.js, Typescript, Redux, Jest, Sass, MUI.

ThinkMoreForum-Frontend Website Desktop Mobile Front-end technology stack Next.js Redux Typescript MUI Axios Husky React testing-library Jest Eslint,

Alan 151 Dec 10, 2022
Max is a Typescript-based Discord bot with many functionalities

Max is a Typescript-based Discord bot with many functionalities. He is both my learning curve for understanding Discord's API as well as my current passion project.

Jack Levreau 4 May 24, 2022
Type-Safe Errors for JS & TypeScript

Type-Safe Errors for JS & TypeScript

Gio 1.4k Jan 6, 2023
A fully type-safe and lightweight way of using exceptions instead of throwing errors

??️ exceptionally A fully type-safe and lightweight way of using exceptions instead of throwing errors ?? fully typesafe ?? lightweight (209 bytes) ??

Hofer Ivan 16 Jan 4, 2023
Framework agnostic CLI tool for routes parsing and generation of a type-safe helper for safe route usage. 🗺️ Remix driver included. 🤟

About routes-gen is a framework agnostic CLI tool for routes parsing and generation of a type-safe helper for safe route usage. Think of it as Prisma,

Stratulat Alexandru 192 Jan 2, 2023
100% type-safe query builder for node-postgres :: Generated types, call any function, tree-shakable, implicit type casts, and more

⚠️ This library is currently in alpha. Contributors wanted! tusken Postgres client from a galaxy far, far away. your database is the source-of-truth f

alloc 54 Dec 29, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
🐬 A simplified implementation of TypeScript's type system written in TypeScript's type system

?? HypeScript Introduction This is a simplified implementation of TypeScript's type system that's written in TypeScript's type annotations. This means

Ronen Amiel 1.8k Dec 20, 2022
Fast and type-safe full stack framework, for TypeScript

Fast and type-safe full stack framework, for TypeScript Why frourio ? Even if you write both the frontend and backend in TypeScript, you can't statica

frourio 1.1k Dec 26, 2022
Opinionated, type-safe, zero-dependency max/min priority queue for JavaScript and TypeScript projects.

qewe qewe is an opinionated, type-safe, zero-dependency max/min priority queue for JavaScript and TypeScript projects. Installation Add qewe to your p

Jamie McElwain 2 Jan 10, 2022